0x00 简介
Pwntools 是一个 CTF 框架和漏洞利用开发,可用于快速编写 exp 脚本。它拥有本地执行、远程连接读写、shellcode生成、ROP链构建、ELF文件解析、符号泄露等众多强大的功能。
- 官方文档:https://pwntools.readthedocs.io/en/stable/
pwntools 分为两个模块,一个是 pwn,可以用 from pwn import * 导入到 python 中使用,该模块是专门针对 CTF 比赛优化的;另一个模块是 pwnlib,它更适合根据需要导入的子模块,常用于基于 pwntools 的二次开发。
0x01 模块简介
- context:设置运行时变量
- elf:操作 ELF 可执行文件和共享库
- tubes:链接及信息传输模块,与 sockets、process、ssh 等进行连接
- constants:包含各种体系结构和操作系统中的常量,如- - constants.linux.i386.SYS_stat
- rop: ROP 利用工具,包括 rop、srop
- gdb:调试,启用 gdb 调试
- util:一些使用的小工具
- log:日志记录管理
- constants:包含各种体系结构和操作系统中的常量,如constants.linux.i386.SYS_stat
- asm:汇编和反汇编
- encoders:对 shellcode 进行编码
- shellcraft:生成各种功能的汇编代码
- runner:运行 shellcode,例如 run_assembly()
- memleak:用于内存泄漏
- dynelf:利用信息泄露远程解析函数
- fmtstr:格式化字符串利用工具
0x02 pwntools 安装
apt-get update
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools
# 可使用国内源提高 pip 下载速度
pip install pip -U
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
0x03 context 模块介绍
该模块用于设置运行时变量,如:目标系统、目标体系结构、字节序、日志等
context.clear() ## 清除当前运行时变量
context.arch = ‘arm’ ## 设置架构:amd64、i386、arm、mips
context.os = ‘linux’ ## 设置操作系统:linux
context.bits=‘32’ ## 设置 32 位或 64 位:32、64
context.endian=‘little’ ## 设置字节序:little、big
context.update(os=‘linux’, arch=‘amd64’, bits=64) ## 更新当前运行时变量
context.log_level=‘debug’ ## 设置日志级别:debug、info
context.log_file=‘/tmp/pwnlog.txt’ ## 设置日志保存文件
0x04 tubes 模块介绍
在漏洞利用中,首先需要与目标文件或者目标服务器进行交互,这就要用到 tubes 模块。
- 常用命令
## 连接
remote('ip',端口):远程
process():本地
## 发送
send(data) : 发送数据
sendline(data) : 发送一行数据,相当于在数据末尾加\n
sendafter(xxx,data):接收到 xxx 之后发送 data
## 接收
recv(numb=4096, timeout=default) : 接收指定字节
recvline(keepends=True) : 接收一行,keepends为是否保留行尾的\n
recvuntil(delims, drop=False) : 一直读到 delims 的 pattern 出现为止
recvrepeat(timeout=default) : 持续接受直到EOF或timeout
## 交互
interactive() 直接进行交互,相当于回到shell的模式,在取得shell之后使用
模块中的其他函数
interactive() ## 交互模式,能够同时读写管道,通常在获得 shell 之后调用
recv(numb=1096, timeout=default) ## 接受最多 numb 字节的数据
recvn(numb, timeout=default) ## 接受 numb 字节的数据
recvall() ## 接受数据直到 EOF
recvline(keepends=True) ## 接受一行,可选择是否保留行尾的 ‘\n’
recvrepeat(timeout=default) ## 接受数据直到 EOF 或 timeout
recvuntil(delims,timeout=default) ## 接受数据直到 delims 出现
send(data) ## 发送数据
sendafter(delim, data, timeout=default) ## 相当于 recvuntil(delim, timeout) 和 send(data) 的组合
sendline(data) ## 发送一行,默认在行尾加 “\n”
sendlineafter(delim, data, timeout=default) ## 相当于 recvuntil(delim, timeout) 和 sendline(data) 的组合
close() ## 关闭管道
0x05 ELF 模块介绍
该模块用于操作 ELF 文件,包括符号查找、虚拟内存、文件偏移,以及修改和保存二进制文件等功能。
## 常用命令
elf = ELF('xx')
elf.address elf 基地址
elf.bss() elf bss 段
elf.plt['write'] 查看 plt[] 表项对应地址
elf.got['write'] 查看 got[] 表项对应地址
elf.symbols['write'] 查看对应符号函数/数据所在地址
elf.search['/bin/sh'] 返回一个迭代器,用next()获取
模块中的其他函数
asm(address, assembly) ## 汇编指令 assemnbly 并将其插入 ELF 的 address 地址处,需要使用 ELF.save() 函数来保存
bss(offset) ## 返回 .bss 段加上 offset 后的地址
checksec() ## 查看文件开启的安全保护
disable_nx() ## 关闭 NX
disasm(address, n_bytes) ## 返回对地址 address 反汇编 n 字节的字符串
offset_to_vaddr(offset) ## 将偏移 offset 转换为文件偏移
vaddr_to_offset(address) ## 将虚拟地址 address 转换为文件偏移
read(address, conut) ## 从虚拟地址 address 读取 count 个字节的数据
write(address, data) ## 在虚拟地址 address 写入data
section(name) ## 获取 name 段的数据
0x06 GDB 模块介绍
在编写漏洞利用的时候,常常需要使用 GDB 动态调试,该模块就提供了这方面的支持。两个常用函数如下:
gdb.attach(target, gdbscript = None):在一个新终端打开 GDB 并 attach 到指定 PID 的进程,或者一个 pwnlib.tubes 对象
gdb.debug(args, gdbscript = None):在新终端中使用 GDB 加载一个二进制文件
gdb.attach(1234)
bash = process(‘./bash’)
gdb.attach(bash, ’’’command’’’)
0x07 util.packing
可以设置小端和大端
p32 数字 -> bytes
p64
u32 bytes -> 数字
u64
0x08 fmt 模块
Pwntools pwnlib.fmtstr 模块提供了一些字符串漏洞利用工具。该模块中定义了一个 类 FmtStr 和一个函数 fmtstr_payload。其中 FmtStr 提供了自动话的字符串漏洞利用。fmtstr_payload 则用于自动生成格式化字符串 payload
class pwnlib.fmtstr.Fmtstr(execute_fmt, offset=None,padlen=0,numbwriteen=0)
## 参数说明
- execute_fmt(function): ## 与漏洞进程进行交互的函数
- offset(int): ## 你控制的第一个格式化程序的偏移量
- padlen(int): ## 在 paload 之前添加的 pad 大小
- numbwriteen(int): ## 已经写入的字节数
class pwnlib.fmtstr.fmtstr_payload(offset,writes, numbwriteen=0, write_size=‘bytes’)
## 参数说明
- offset(int): ## 你控制的第一个格式化程序的偏移量
- writes(dict): ## 格式为{addr1:value,addr2:value2},用于往 addr 里写入 value 值(常用:printf_got)
- numbwriteen(int):## 已经由 printf 写入的字节数
- write_size(str): ## 必须是 bytes、short 或 int。
0x09 shellcode 相关模块介绍
导入 pwntools 库,并使用对用函数生成 getshell 的汇编代码
使用 pwntools 中的 asm 对 sh 进行汇编。生成二进制代码:
0x0A Dynelf 相关模块介绍
Dynelf 是 pwntools 工具库中的一个类,使用 DynELF 时,我们需要使用一个 leak 函数作为必选参数。并且至少要提供一个指向目标 ELF 文件的指针或者目标ELF 文件通过 ELF 类实例化的对象(二者选其一),作为可选参数,以此来初始化一个 DynELF 类。
leak 函数需要使用目标程序本身的漏洞,泄露出 int 型参数 addr 对应的内存地址中的数据。由于 DynELF 会多次调用leak函数,这个 leak 函数必须能任意次使用,即不能泄露几个地址之后就导致程序崩溃。
## leak 函数模板
def leak(addr): ## addr 类型 int
payload = xxx
p.send (payload)
leak_data = p.recv(4) ## 32位
## leak_data = p.recv(8) ## 64位
print("%#x -> %s" %(addr, (leak_data or ''))))
return leak_data ## leak_data 必须返回 Bytes 类型的值
Dynelf:用法
dynelf_obj = DynELF(leak, elf=elf)
system_addr = dynelf_obj .lookup('system', 'libc')
success("system: %#x" %system_addr)
read_addr = dynelf_obj .lookup('read', 'libc')
success("read: %#x" %read_addr)
0x0B ROP 模块介绍
该模块可以帮助我们简单快速的生成 ROP chain
## 相关函数:
R = ROP(elfs(list), base(int), badchars(str)) 类初始化
R.raw() ## 直接添加原始数据
R.call(funcname, args(list)) ## 调用某函数
R.funcname(arg1,rag2) ## 调用某函数
R.dump() ## 显示 ROP chain 的内容
hexdump(r.chain()) ## 输出 ROP chain 信息
R.gadgets ## 显示所有 gadgets
R.rax ## 显示与 rax 相关的 gadget 返回 Gadget 类
R.syscall ## 显示 syscall gadget
R.search() ## 按某条件搜索 gadget
R.chain() ## 生成 ROP chain 原理是通过 flat 进行封装
除了上述基础函数外, ROP 模块还可以自动生成某些攻击类型的 ROP chain,例如 ret2csu 的攻击链、栈迁移的攻击链、ret2dlresolver 的攻击链、SROP 的 SigreturnFrame。
-
ret2dlresolver 的使用方法
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"], data_addr = None) ## 生成对应的 ROP Chain rop.ret2dlresolve(dlresolve) ## 生成的 fake_data dlresolve.payload ## 保存 fake_data 的地址,默认会自动生成一个地址,所以我们在调用 read 时可以使用其默认的地址, ## 但是我们也可以在初始化时传入一个地址 dlresolve.data_addr: