泄露原理
泄露原理
1 | puts / printf |
de1ctf_2019_weapon
https://buuoj.cn/challenges#de1ctf_2019_weapon
伪造fd和size拿到这个位置的chunk

然后通过该chunk,把chunk2的size改为0xd1,覆盖下面两个chunk

1 | add(0x50, 0, b"a") |
然后把chunk23依次free,放入unsorted bin和fast bin,由于chunk2包含chunk3,可以通过malloc unsored 让chunk3的fd变为main_arena

1 | free(2) |
之后依旧double free ,把chunk3的fd划为可覆盖的范围内,改fd到_IO_2_1_stdout_附近,根据不同去覆盖后几位,然后修改stdout

1 | free(2) |
之后就是覆盖malloc_hook了
1 | malloc_hook = libc_base + libc.sym['__malloc_hook'] |
exp
1 | from pwn import * |
ctfshow pwn164


delete函数中有一个uaf漏洞

清空ptr指针

realloc chunk
realloc(ptr, size)的关键逻辑这行代码是整道题的核心。由于
ptr是全局的,每次调用add都会发生以下三种情况之一:
- 情况 A:首次调用 (
ptr初始为 0)realloc(0, size)的行为等同于malloc(size)。它申请一块内存,并将地址存入全局变量ptr。- 情况 B:扩容/缩容 (
ptr已有地址,size > 0)realloc会尝试在原地调整ptr指向的块大小。如果原地空间不够,它会开辟新块、拷贝原数据、释放(free)旧块,最后把新地址覆盖回ptr。- 情况 C:释放内存 (
ptr已有地址,size == 0) 这是这道题最关键的漏洞点。根据realloc的标准,realloc(ptr, 0)等同于执行free(ptr),并且会返回NULL(0)。 由于代码里有ptr = realloc(ptr, size),执行完后全局变量ptr会被赋值为0。
分析
由于没有show函数,还是要通过stdout去泄露libc,这里用到了ralloc会在原地拓展的属性,拿到目标chunk的上一个chunk去calloc一个大的size去覆盖main_arena的低2个字节进行爆破
先摆好tar和tar_pre两个chunk的位置
然后通过7次free把tcache bin塞满(2.27的tcache没有引入key,可以随便free),并且把tar_chunk free掉准备main_arena
接着拿tar_pre的地址,用这个地址去拓展,造成堆块重叠去改main_arena的后几位去爆破
1 | add(0x70,b"a") |

这里不能用sudo sysctl -w kernel.randomize_va_space=0
因为他这个偏移比较奇怪会导致_IO_2_1_stdout_和main_arena不是在同一个0x10000上,无法通过覆盖后两位过去
拿到libc之后就可以通过同样的办法覆盖free_hook
这里因为ralloc返回的地址是free函数的参数
所以我们可以改fd为free_hook-8,然后覆盖b”/bin/sh\x00”+p64(system)
让realloc之后的参数为binsh的地址,并且把free_hook改为system函数
1 | free= libc_base + libc.sym['__free_hook'] |

exp
1 | from pwn import * |