pwntools 英文文档
pwntools 中文文档

安装

环境准备

python
pip
libssl-dev
libffi-dev
sudo apt install libssl-dev
sudo apt install libffi-dev
sudo pip install pwntools

利用

以下只是常用命令

导入

导入pwntools模块

from pwn import *

context

Log等级

context.log_level = "debug" # 打印调试信息
context.log_level = "error" # 打印错误信息, 此时很多回显就看不到了

或者在调用时使用 DEBUG 参数

python3 exp.py DEBUG

指定cpu类型, 操作系统类型, 端序, 位数

context.arch      = 'i386'
context.os = 'linux'
context.endian = 'little'
context.word_size = 32

在tmux中以分屏的形式启动gdb

context.terminal = ['tmux', 'splitw', '-h'] # 横向
context.terminal = ['tmux', 'splitw', '-v'] # 纵向

启动程序

p = process("./pwn") # 本地
p = remote(ip, port) # e.g. p = remote("10.10.10.10", 23946) 远程交互

绑定libc

p = process(['./bin'], env={'LOAD_PRELOAD': './libc-2.23.so'})

gdb调试

gdb.attach(p, "b read") # "b read"为gdb开启后执行的命令

IO模块

发送信息

p.send(data) # 不带回车
p.sendline(data) # 带回车
p.sendafter(delim, data, timeout = default) # recvuntil(delim, timeout = timeout)和send(data)的组合。
p.sendlineafter(delim, data, timeout = default) # recvuntil(delim, timeout = timeout)和sendline(data)的组合。

接受信息

p.recv(number) # 接收number个字节的信息, number可省略
p.recvline() # 接收一行信息
p.recvuntil(msg) # 接受信息直到msg出现

启动交互

p.interactive()

打印信息

log.success("msg")
log.info("msg")

Shellcode 生成器

shellcraft.i386.linux.sh()

打印出来:

>>> print(shellcraft.i386.linux.sh())
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push b'/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp
/* push argument array ['sh\x00'] */
/* push 'sh\x00\x00' */
push 0x1010101
xor dword ptr [esp], 0x1016972
xor ecx, ecx
push ecx /* null terminate */
push 4
pop ecx
add ecx, esp
push ecx /* 'sh\x00' */
mov ecx, esp
xor edx, edx
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80

结合 asm 可以得到最终的 payload

from pwn import *
context(os='linux',arch='amd64')
shellcode = asm(shellcraft.sh())

或者

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

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

pack and unpack

pack: p8, p16, p32, p64
unpack: u8, u16, u32, u64

示例

payload = 'a' * 0x80 + p64(sys_addr)


__malloc_hook_addr = u64(p.recv(6).ljust(8, "\x00"))

ROP链生成器

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对象实现了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) : 给出一个地址,反解析出符号

常用模板

from pwn import *

elf_name = "./filename"

p = process([elf_name], env = {"LD_PRELOAD": "./libc.so.6"})

elf = ELF(elf_name)
libc = ELF("./libc.so.6")

context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-v']

rv = p.recv
ru = p.recvuntil
sd = p.send
sa = p.sendafter
sl = p.sendline
sla = p.sendlineafter

def dbg(breakpoint):
gdb.attach(p, "b " + breakpoint)

p.interactive()

参考

pwntools使用