0x01 简介

pwntools是一个CTF Pwn漏洞利用开发库,用于编写各种与Pwn题目进行交互和攻击利用的脚本。其由Python开发,由rapid设计,旨在让使用者简单快速的编写exploit。

0x02 安装

安装:pip install pwntools

当然只是在Linux环境适用,建议安装在Ubuntu环境而非Kali,Kali上会有很多问题。其中对Ubuntu 12.04和14.04的支持最好,但是绝大多数的功能也支持Debian、Arch、FreeBSD、OSX等等。

0x03 各个模块

通常在py文件头部写上from pwn import *即可导入所有需要的pwntools工具。

连接方式

1
2
3
sh = porcess("./test")#连接本地程序
sh = remote("127.0.0.1",10001)#远程连接
sh.close()#关闭连接

IO模块

1
2
3
4
5
6
7
8
sh.send(data)#发送数据
sh.sendline(data)#发送一行数据,相当于在数据后面加\n
sh.recv(numb = 2048, timeout = dufault)#接受数据,numb指定接收的字节,timeout指定超时时间
sh.recvline(keepends=True)#接受一行数据,keepends为是否保留行尾的\n
sh.recvuntil("Hello,World\n",drop=fasle)#接受数据直到我们设置的标志出现
sh.recvall()#一直接收直到EOF
sh.recvrepeat(timeout = default)#持续接受直到EOF或timeout
sh.interactive()#直接进行交互,相当于回到shell的模式,在取得shell之后使用

ELF文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> e = ELF('/bin/cat')
>>> print hex(e.address) # 文件装载的基地址
0x400000
>>> print hex(e.symbols['write']) # 函数地址
0x401680
>>> print hex(e.got['write']) # GOT表的地址
0x60b070
>>> print hex(e.plt['write']) # PLT的地址
0x401680
>>> print hex(e.search('/bin/sh').next())# 字符串/bin/sh的地址

#libc相关
libc = e.libc
libc.address = libc_start_main_addr - libc.symbols['__libc_start_main']
libc.symbols['system']
next(libc.search('/bin/sh'))

整数pack与数据unpack

pack:p32,p64
unpack:u32,u64

汇编和反汇编

汇编:

1
2
3
4
>>> asm('nop')
'\x90'
>>> asm('nop', arch='arm')
'\x00\xf0 \xe3'

可以使用context来指定cpu类型以及操作系统:

1
2
3
4
>>> context.arch      = 'i386'
>>> context.os = 'linux'
>>> context.endian = 'little'
>>> context.word_size = 32

使用disasm进行反汇编:

1
2
3
4
5
>>> print disasm('6a0258cd80ebf9'.decode('hex'))
0: 6a 02 push 0x2
2: 58 pop eax
3: cd 80 int 0x80
5: eb f9 jmp 0x0

注意,asm需要binutils中的as工具辅助,如果是不同于本机平台的其他平台的汇编,例如在我的x86机器上进行mips的汇编就会出现as工具未找到的情况,这时候需要安装其他平台的cross-binutils。

Shellcode生成器

1
2
3
4
5
6
7
8
>>> print shellcraft.i386.nop().strip('\n')
nop
>>> print shellcraft.i386.linux.sh()
/* push '/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
...

结合asm可以可以得到最终的pyaload:

1
2
3
4
5
6
7
8
from pwn import *
context(os='linux',arch='amd64')
shellcode = asm(shellcraft.sh())

或者

from pwn import *
shellcode = asm(shellcraft.amd64.linux.sh())

除了直接执行sh之外,还可以进行其它的一些常用操作例如提权、反向连接等等。

ROP链生成器

1
2
3
4
5
6
7
8
9
10
elf = ELF('ropasaurusrex')
rop = ROP(elf)
rop.read(0, elf.bss(0x80))
rop.dump()
# ['0x0000: 0x80482fc (read)',
# '0x0004: 0xdeadbeef',
# '0x0008: 0x0',
# '0x000c: 0x80496a8']
str(rop)
# '\xfc\x82\x04\x08\xef\xbe\xad\xde\x00\x00\x00\x00\xa8\x96\x04\x08'

使用ROP(elf)来产生一个rop的对象,这时rop链还是空的,需要在其中添加函数。

因为ROP对象实现了getattr的功能,可以直接通过func call的形式来添加函数,rop.read(0, elf.bss(0x80))实际相当于rop.call(‘read’, (0, elf.bss(0x80)))。
通过多次添加函数调用,最后使用str将整个rop chain dump出来就可以了。

  • call(resolvable, arguments=()) : 添加一个调用,resolvable可以是一个符号,也可以是一个int型地址,注意后面的参数必须是元组否则会报错,即使只有一个参数也要写成元组的形式(在后面加上一个逗号)
  • chain() : 返回当前的字节序列,即payload
  • dump() : 直观地展示出当前的rop chain
  • raw() : 在rop chain中加上一个整数或字符串
  • search(move=0, regs=None, order=’size’) : 按特定条件搜索gadget
  • unresolve(value) : 给出一个地址,反解析出符号

GDB调试

当在运行py时添加参数M,则会将进程attach到GDB上调试:

1
2
if args.M:
gdb.attach(p)

DEBUG日志

当 context.log_level被设置为 “DEBUG” , 我们的输入和服务器的输出会被直接输出:

1
context.log_level = 'DEBUG'

0x04 参考

pwntools使用