pwntools的使用学习

 pwntools是一个ctf框架和漏洞利用开发库,用Python开发,旨在让使用者简单快速的编写exploit。

安装请点这里

常用模块

tubes : 包括tubes.sock, tubes.process, tubes.ssh, tubes.serialtube,分别适用于不同场景的PIPE
elf : 对elf文件进行操作,可以获取elf文件中的PLT条目和GOT条目信息
dynelf :用于远程符号泄漏,需要提供leak方法
memleak : 用于内存泄漏
asm : 汇编与反汇编,支持x86/x64/arm/mips/powerpc等基本上所有的主流平台
shellcraft : shellcode的生成器

Tubes读写接口

 与目标进行交互是一次攻击的最基本的操作,利用pwntools的远程调试remote(address,port)和本地调试process(path)可以和目标产生一个socket进行读写

1
2
3
4
5
6
7
8
interactive() : 直接进行交互,相当于回到shell的模式,在取得shell之后使用
recv(numb = 4096,timeout = default):接收指定字节
recvall() : 一直接收直到EOF
recvline(keepends = True): 接收一行,keepends为是否保留行尾的\n,默认为Ture
recvuntil((delims,drop=False):一直读到delims的pattern出现为止
recvrepeat(timeout=default): 持续接收知道EOF或者timeout
send(data) :发送数据
sendline(data) : 发送一行数据,相当于在数据末尾加\n

ELF

 这是一个非常实用的模块,下面演示了:获取基地址、获取函数地址(基于符号)、获取函数got地址、获取函数plt地址

1
2
3
4
5
6
7
8
9
>>> e = ELF('/bin/cat')
>>> print hex(e.address)
0x400000
>>> print hex(e.symbols['write'])
0x401680
>>> print hex(e.got['write'])
0x60b070
>>> print hex(e.plt['write'])
0x401680

p32、p64是将数据打包(将整数转换为二进制字符串)
u32、u64是将解包(将二进制字符串转换为整数)
 ELF模块还可以修改ELF文件,这里就不多介绍了,详情参考官方文档

Dynelf

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

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
26
27
28
29
30
31
32
33
34
35
36
# Assume a process or remote connection
p = process('./pwnme')
# Declare a function that takes a single address, and
# leaks at least one byte at that address.
def leak(address):
data = p.read(address, 4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
# For the sake of this example, let's say that we
# have any of these pointers. One is a pointer into
# the target binary, the other two are pointers into libc
main = 0xfeedf4ce
libc = 0xdeadb000
system = 0xdeadbeef
# With our leaker, and a pointer into our target binary,
# we can resolve the address of anything.
#
# We do not actually need to have a copy of the target
# binary for this to work.
d = DynELF(leak, main)
assert d.lookup(None, 'libc') == libc
assert d.lookup('system', 'libc') == system
# However, if we *do* have a copy of the target binary,
# we can speed up some of the steps.
d = DynELF(leak, main, elf=ELF('./pwnme'))
assert d.lookup(None, 'libc') == libc
assert d.lookup('system', 'libc') == system
# Alternately, we can resolve symbols inside another library,
# given a pointer into it.
d = DynELF(leak, libc + 0x1234)
assert d.lookup('system') == system

详细利用方法可参考 https://www.anquanke.com/post/id/85129

汇编与反汇编

1
2
3
4
>>> asm('nop')
'\x90'
>>> asm('nop', arch='arm')
'\x00\xf0 \xe3'
1
2
3
4
5
6
7
>>> disasm('\xb8\x0b\x00\x00\x00')
' 0: b8 0b 00 00 00 mov eax,0xb'
>>> print disasm('6a0258cd80ebf9'.decode('hex'))
0: 6a 02 push 0x2
2: 58 pop eax
3: cd 80 int 0x80
5: eb f9 jmp 0x0

shellcode

 其中的子模块声明架构,比如shellcraft.arm 是ARM架构的,shellcraft.amd64是AMD64架构,shellcraft.i386是Intel 80386架构的,以及有一个shellcraft.common是所有架构通用的。
 由于平台和32位和64位的shellcode不一样,所以最好先设置context。

1
2
3
>>> x = shellcraft.i386() #32位linux的shellcode
>>> print(shellcraft.sh()) #打印shellcode
>>> x = asm(x) #把shellcode编程汇编形式

更多信息请参考官方文档