pwntools 笔记

Pwntools 简述

Pwntools 是一个 CTF 框架和漏洞利用开发库。

它是用 Python 编写的,专为快速原型设计和开发而设计,旨在使漏洞利用编写尽可能简单。

我们可以使用 pip 包管理工具安装 pwntools

1
pip3 install pwntools

pwntools 需要及时使用最新版,我们可以使用以下命令对 pwntools 更新:

1
python3 -m pip install --upgrade pwntools

Pwntools 详解

pwntools 模块索引

模块名称 模块功能
pwnlib.adb adb 调试桥的封装
pwnlib.args 包含命令行参数的一些配置 (包含这个包后会自动从命令行中获取参数)
pwnlib.asm 通过 capstone 进行汇编的编译和反编译等功能的封装
pwnlib.atexception 包含未处理异常的回调函数
pwnlib.atexit atexit 的替代品
pwnlib.constants 头文件宏定义的封装,可以通过这个模块直接获取系统宏定义常数
pwnlib.config Pwntools 配置变量
pwnlib.context 包含运行时变量,上下文管理器
pwnlib.dynelf 通过内存 leak 数据自动化定位程序中函数位置
pwnlib.encoders Shellcode 编码工具封装
pwnlib.elf.config elf 核心配置
pwnlib.elf.corefile elf 核心文件转储功能
pwnlib.elf.elf elf 文件操作封装
pwnlib.exception Pwnlib 异常
pwnlib.filepointer FILE* 结构体封装
pwnlib.filesystem 通过 SSH 为本地文件系统和远程文件系统上的路径提供 Python2 的兼容接口
pwnlib.flag CTF Flag 管理
pwnlib.fmtstr 格式化字符串漏洞利用封装
pwnlib.gdb GDB 调试封装
pwnlib.libcdb Libc 数据库,可以通过一些信息查找对应的地址
pwnlib.log 日志库,跟 Pwntools 的操作
pwnlib.memleak 内存泄漏管理工具
pwnlib.qemu QEMU 适用工具封装
pwnlib.replacements 一些标准函数的替代品
pwnlib.rop.ret2dlresolve 提供缓冲区溢出利用的负载自动化生成工具
pwnlib.rop.rop ROP 生成工具,可以直接生成 32 位 ROP
pwnlib.rop.srop Sigreturn 相关的利用封装
pwnlib.runner Shellcode 运行封装
pwnlib.shellcraft Shellcode 生成工具
pwnlib.shellcraft.aarch64 Shellcode for AArch64
pwnlib.shellcraft.amd64 Shellcode for AMD64
pwnlib.shellcraft.arm Shellcode for ARM
pwnlib.shellcraft.common Shellcode 所有架构的基本利用
pwnlib.shellcraft.i386 Shellcode for Intel 80386
pwnlib.shellcraft.mips Shellcode for MIPS
pwnlib.shellcraft.thumb Shellcode for Thumb Mode
pwnlib.term 终端处理封装
pwnlib.timeout 超时处理封装
pwnlib.tubes 套接字、SSH 等 IO 封装
pwnlib.tubes.buffer IO 缓冲封装
pwnlib.tubes.process IO 进程封装
pwnlib.tubes.serialtube IO 串行端口封装
pwnlib.tubes.sock 套接字封装
pwnlib.tubes.ssh SSH 封装
pwnlib.ui UI 封装
pwnlib.update 更新 Pwntools
pwnlib.useragents useragent 生成封装
pwnlib.util.crc 计算一系列 CRC 的封装
pwnlib.util.cyclic 一些序列生成器的封装
pwnlib.util.fiddling 编码封装
pwnlib.util.getdents Linux 二进制目录处理
pwnlib.util.hashes 哈希散列函数封装
pwnlib.util.iters itertools 的扩展
pwnlib.util.lists 对 list 的操作封装
pwnlib.util.misc 一些没办法分类的杂项函数封装
pwnlib.util.net 网络交互封装
pwnlib.util.packing 压缩操作封装
pwnlib.util.proc /proc/ 相关的进程操作封装
pwnlib.util.safeeval 安全的 eval()
pwnlib.util.sh_string Shell 字符串 eval()
pwnlib.util.web web 网络交互工具

推荐使用 from pwn import * 将以上各功能模块直接导入

Pwntools 与服务器

使用 Pwntools 与服务器进行交互,需要利用 pwnlib.tubes 库,方便起见可以直接 from pwn import * 导入所有模块。

建立连接

建立连接我们需要使用 remote 类,它的构造函数需要接收域名 (host) 和 端口 (port) 来跟服务器建立 TCP 或 UDP 连接,相当于 Linux 中的 nc 指令。

其他参数请参考官方文档 http://docs.pwntools.com/en/latest/tubes.html

使用示例:

1
2
io = pwn.remote("127.0.0.1", 8888)
client = pwn.remote("Localhost", 8888)

接收数据

  • recv(numb, timeout)

    numb → 接收的字节数;有默认值

    timeout → 接收时限,单位秒,超时则中断接收;有默认值

    @returns → 返回一个保存所接收数据的 bytes 数组

    如果不给参数,默认返回缓冲区数据

    使用示例:

    1
    2
    3
    4
    5
    6
    7
    8
    # 接收所有数据 (但只是缓冲区内容)
    data = io.recv()
    # 接收 1024 字节数据
    data = io.recv(1024)
    # 接收 1 秒数据
    data = io.recv(timeout=1)
    # 接收 1024 字节数据,如果超时则直接返回
    data = io.recv(1024, timeout=1)
  • recvall(timeout)

    timeout → 接收时限,单位秒,超时则中断接收;默认 forever

    @returns → 返回一个保存所接收数据的 bytes 数组

    使用示例:

    1
    2
    3
    4
    # 接收所有数据,直到 socket 关闭
    data = io.recvall()
    # 接收 1 秒数据
    data = io.recvall(timeout=1)
  • recvline(keepends, timeout)

    keepends → 接收数据是否保留回车;默认保留

    timeout → 接收时限,单位秒,超时则中断接收;有默认值

    @returns → 返回一个保存所接收数据的 bytes 数组

    使用示例:

    1
    2
    3
    4
    # 接收一行数据 (但含有'\n')
    data = io.recvline()
    # 接收一行数据 (不含有'\n')
    data = io.recvline(False)
  • recvuntil(delims, drop, timeout)

    delims → 接收中止标志

    drop → 是否丢弃中止标志;默认不丢弃

    timeout → 接收时限,单位秒,超时则中断接收;有默认值

    @returns → 返回一个保存所接收数据的 bytes 数组

    使用示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    """
    接收数据直到接收到 'start' 后结束,字符串中包含 'start'
    比如缓冲区中存在数据 'the game is started!'
    那么函数会返回 b'the game is start',缓冲区为 'ed!'
    """
    data = io.recvuntil('start')
    """
    接收数据直到接收到 'start' 后结束,字符串中不包含 'start'
    比如缓冲区中存在数据 'the game is started!'
    那么函数会返回 b'the game is ',而缓冲区为 'ed!'
    """
    data = io.recvuntil('start', True)

发送数据

  • send(data)

    data → 发送的 bytes 数组

    使用示例:

    1
    2
    # 发送数据,但不包含'\n'
    io.send('hey!')
  • sendline(data)

    data → 发送的 bytes 数组,默认值为换行符

    使用示例:

    1
    2
    # 发送数据,且包含换行符
    io.sendline('hey!')
  • sendthen(delim, data, timeout)

    delims → 接收中止标志

    data → 发送的 bytes 数组

    timeout → 接收时限,单位秒,超时则中断接收;有默认值

    @returns → 返回一个保存所接收数据的 bytes 数组

    sendlinethen 同理

    使用示例:

    1
    2
    3
    4
    # 发送数据'hey!',然后 io.recvuntil('how are u?')
    data = io.sendthen('how are u?', 'hey!')
    # 发送数据'hey!',然后 io.recvuntil('how are u?', 1)
    data = io.sendthen('how are u?', 'hey!', 1)
  • sendafter(delim, data, timeout)

    delims → 接收中止标志

    data → 发送的 bytes 数组

    timeout → 接收时限,单位秒,超时则中断接收;有默认值

    @returns → 返回一个保存所接收数据的 bytes 数组

    sendlinethen 同理

    使用示例:

    1
    2
    3
    4
    # io.recvuntil('how are u?'),然后接收数据'I am fine.'
    data = io.sendafter('how are u?', 'I am fine.')
    # io.recvuntil('how are u?', 1),然后接收数据'I am fine.'
    data = io.sendafter('how are u?', 'hey!', 1)

交互模式

remote.interactive()

可以直接进入终端交互模式,自动接收数据并输出,且会等待输入。

使用示例:

1
io.interactive()

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *

# 与 127.0.0.1:8888 建立tcp连接
io = remote("127.0.0.1", 8888)
# 接收一行数据(例如 b'how are u?\n')并解码为str类型
msg = io.recvline().decode()
print(f"recv: {msg}")

# 发送一行数据(即发送 b'I am fine. are u?\n')
io.sendline("I am fine. are u?".encode())
# 接收一行数据(例如 b'I am fine, too.\n')并解码为str类型
msg = io.recvline().decode()
print(f"recv: {msg}")

# 当接收的数据不是 "goobye!" 的时候循环
while msg != "goodbye!":
# 发送一行数据(即发送 b'baka!\n')
io.sendline("baka!".encode())
# 接收一行数据(例如 b'???\n')并解码为str类型
msg = io.recvline().decode()
print(f"recv: {msg}")

# 结束连接
io.close()