2017-10-11 17:22:24 +07:00
|
|
|
|
# 2.7 Pwntools
|
2017-08-17 21:36:23 +07:00
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
- [安装](#安装)
|
|
|
|
|
- [模块简介](#模块简介)
|
|
|
|
|
- [使用 Pwntools](#使用-pwntools)
|
|
|
|
|
- [Pwntools 在 CTF 中的运用](#pwntools-在-ctf-中的运用)
|
|
|
|
|
- [参考资料](#参考资料)
|
2017-08-17 21:36:23 +07:00
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
Pwntools 是一个 CTF 框架和漏洞利用开发库,用 Python 开发,由 rapid 设计,旨在让使用者简单快速的编写 exp 脚本。包含了本地执行、远程连接读写、shellcode 生成、ROP 链的构建、ELF 解析、符号泄露众多强大功能。
|
2017-08-17 21:36:23 +07:00
|
|
|
|
|
|
|
|
|
## 安装
|
|
|
|
|
|
|
|
|
|
1. 安装binutils:
|
|
|
|
|
```shell
|
|
|
|
|
git clone https://github.com/Gallopsled/pwntools-binutils
|
|
|
|
|
sudo apt-get install software-properties-common
|
|
|
|
|
sudo apt-add-repository ppa:pwntools/binutils
|
|
|
|
|
sudo apt-get update
|
|
|
|
|
sudo apt-get install binutils-arm-linux-gnu
|
|
|
|
|
```
|
|
|
|
|
2. 安装capstone:
|
|
|
|
|
```shell
|
|
|
|
|
git clone https://github.com/aquynh/capstone
|
|
|
|
|
cd capstone
|
|
|
|
|
make
|
|
|
|
|
sudo make install
|
|
|
|
|
```
|
|
|
|
|
3. 安装pwntools:
|
|
|
|
|
```shell
|
|
|
|
|
sudo apt-get install libssl-dev
|
|
|
|
|
sudo pip install pwntools
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
如果你在使用 Arch Linux,则可以通过 AUR 直接安装,这个包目前是由我维护的,如果有什么问题,欢迎与我交流:
|
|
|
|
|
```
|
|
|
|
|
$ yaourt -S python2-pwntools
|
2017-08-17 21:36:23 +07:00
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
或者
|
|
|
|
|
|
|
|
|
|
$ yaourt -S python2-pwntools-git
|
|
|
|
|
```
|
2017-10-12 16:08:08 +07:00
|
|
|
|
但是由于 Arch 没有 PPA 源,如果想要支持更多的体系结构(如 arm, aarch64 等),只能手动编译安装相应的 binutils,使用下面的脚本,注意将变量 `V` 和 `ARCH` 换成你需要的。[binutils](https://ftp.gnu.org/gnu/binutils/)
|
|
|
|
|
```bash
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
|
|
|
|
V = 2.29 # binutils version
|
|
|
|
|
ARCH = arm # target architecture
|
|
|
|
|
|
|
|
|
|
cd /tmp
|
|
|
|
|
wget -nc https://ftp.gnu.org/gnu/binutils/binutils-$V.tar.xz
|
|
|
|
|
wget -nc https://ftp.gnu.org/gnu/binutils/binutils-$V.tar.xz.sig
|
|
|
|
|
|
|
|
|
|
# gpg --keyserver keys.gnupg.net --recv-keys C3126D3B4AE55E93
|
|
|
|
|
# gpg --verify binutils-$V.tar.xz.sig
|
|
|
|
|
|
|
|
|
|
tar xf binutils-$V.tar.xz
|
|
|
|
|
|
|
|
|
|
mkdir binutils-build
|
|
|
|
|
cd binutils-build
|
|
|
|
|
|
|
|
|
|
export AR=ar
|
|
|
|
|
export AS=as
|
|
|
|
|
|
|
|
|
|
../binutils-$V/configure \
|
|
|
|
|
--prefix=/usr/local \
|
|
|
|
|
--target=$ARCH-unknown-linux-gnu \
|
|
|
|
|
--disable-static \
|
|
|
|
|
--disable-multilib \
|
|
|
|
|
--disable-werror \
|
|
|
|
|
--disable-nls
|
|
|
|
|
|
|
|
|
|
make
|
|
|
|
|
sudo make install
|
|
|
|
|
```
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
测试安装是否成功:
|
2017-08-17 21:36:23 +07:00
|
|
|
|
```python
|
2017-10-12 16:08:08 +07:00
|
|
|
|
>>> from pwn import *
|
|
|
|
|
>>> asm('nop')
|
|
|
|
|
'\x90'
|
|
|
|
|
>>> asm('nop', arch='arm')
|
|
|
|
|
'\x00\xf0 \xe3'
|
2017-08-17 21:36:23 +07:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
## 模块简介
|
|
|
|
|
Pwntools 分为两个模块,一个是 `pwn`,简单地使用 `from pwn import *` 即可将所有子模块和一些常用的系统库导入到当前命名空间中,是专门针对 CTF 比赛的;而另一个模块是 `pwnlib`,它更推荐你仅仅导入需要的子模块,常用于基于 pwntools 的开发。
|
|
|
|
|
|
|
|
|
|
下面是 pwnlib 的一些子模块(常用模块和函数加粗显示):
|
|
|
|
|
- `adb`:安卓调试桥
|
|
|
|
|
- `args`:命令行魔法参数
|
|
|
|
|
- **`asm`**:汇编和反汇编,支持 i386/i686/amd64/thumb 等
|
|
|
|
|
- `constants`:对不同架构和操作系统的常量的快速访问
|
|
|
|
|
- `config`:配置文件
|
|
|
|
|
- `context`:设置运行时变量
|
|
|
|
|
- **`dynelf`**:用于远程函数泄露
|
|
|
|
|
- `encoders`:对 shellcode 进行编码
|
|
|
|
|
- **`elf`**:用于操作 ELF 可执行文件和库
|
|
|
|
|
- `flag`:提交 flag 到服务器
|
|
|
|
|
- **`fmtstr`**:格式化字符串利用工具
|
|
|
|
|
- **`gdb`**:与 gdb 配合使用
|
|
|
|
|
- `libcdb`:libc 数据库
|
|
|
|
|
- `log`:日志记录
|
|
|
|
|
- **`memleak`**:用于内存泄露
|
2017-10-12 16:08:08 +07:00
|
|
|
|
- **`rop`**:ROP 利用模块,包括 rop 和 srop
|
2017-10-11 17:22:24 +07:00
|
|
|
|
- `runner`:运行 shellcode
|
|
|
|
|
- **`shellcraft`**:shellcode 生成器
|
|
|
|
|
- `term`:终端处理
|
|
|
|
|
- `timeout`:超时处理
|
|
|
|
|
- **`tubes`**:能与 sockets, processes, ssh 等进行连接
|
|
|
|
|
- `ui`:与用户交互
|
|
|
|
|
- `useragents`:useragent 字符串数据库
|
|
|
|
|
- **`util`**:一些实用小工具
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 使用 Pwntools
|
|
|
|
|
下面我们对常用模块和函数做详细的介绍。
|
|
|
|
|
|
|
|
|
|
#### tubes
|
|
|
|
|
在一次漏洞利用中,首先当然要与二进制文件或者目标服务器进行交互,这就要用到 tubes 模块。
|
|
|
|
|
|
|
|
|
|
主要函数在 `pwnlib.tubes.tube` 中实现,子模块只实现某管道特殊的地方。四种管道和相对应的子模块如下:
|
|
|
|
|
- `pwnlib.tubes.process`:进程
|
|
|
|
|
- `>>> p = process('/bin/sh')`
|
|
|
|
|
- `pwnlib.tubes.serialtube`:串口
|
|
|
|
|
- `pwnlib.tubes.sock`:套接字
|
|
|
|
|
- `>>> r = remote('127.0.0.1', 1080)`
|
|
|
|
|
- `>>> l = listen(1080)`
|
|
|
|
|
- `pwnlib.tubes.ssh`:SSH
|
|
|
|
|
- `>>> s = ssh(host='example.com`, user='name', password='passwd')`
|
|
|
|
|
|
|
|
|
|
`pwnlib.tubes.tube` 中的主要函数:
|
|
|
|
|
- `interactive()`:可同时读写管道,相当于回到 shell 模式进行交互,在取得 shell 之后调用
|
|
|
|
|
- `recv(numb=1096, timeout=default)`:接收指定字节数的数据
|
|
|
|
|
- `recvall()`:接收数据直到 EOF
|
|
|
|
|
- `recvline(keepends=True)`:接收一行,可选择是否保留行尾的 `\n`
|
|
|
|
|
- `recvrepeat(timeout=default)`:接收数据直到 EOF 或 timeout
|
|
|
|
|
- `recvuntil(delims, timeout=default)`:接收数据直到 delims 出现
|
|
|
|
|
- `send(data)`:发送数据
|
|
|
|
|
- `sendline(data)`:发送一行,默认在行尾加 `\n`
|
|
|
|
|
- `close()`:关闭管道
|
|
|
|
|
|
|
|
|
|
下面是一个例子,先使用 listen 开启一个本地的监听端口,然后使用 remote 开启一个套接字管道与之交互:
|
2017-10-12 16:08:08 +07:00
|
|
|
|
```text
|
|
|
|
|
>>> from pwn import *
|
|
|
|
|
>>> l = listen()
|
2017-10-11 17:22:24 +07:00
|
|
|
|
[x] Trying to bind to 0.0.0.0 on port 0
|
|
|
|
|
[x] Trying to bind to 0.0.0.0 on port 0: Trying 0.0.0.0
|
|
|
|
|
[+] Trying to bind to 0.0.0.0 on port 0: Done
|
2017-10-12 16:08:08 +07:00
|
|
|
|
[x] Waiting for connections on 0.0.0.0:46147
|
|
|
|
|
>>> r = remote('localhost', l.lport)
|
|
|
|
|
[x] Opening connection to localhost on port 46147
|
|
|
|
|
[x] Opening connection to localhost on port 46147: Trying ::1
|
|
|
|
|
[x] Opening connection to localhost on port 46147: Trying 127.0.0.1
|
|
|
|
|
[+] Opening connection to localhost on port 46147: Done
|
|
|
|
|
>>> [+] Waiting for connections on 0.0.0.0:46147: Got connection from 127.0.0.1 on port 38684
|
|
|
|
|
|
|
|
|
|
>>> c = l.wait_for_connection()
|
|
|
|
|
>>> r.send('hello\n')
|
|
|
|
|
>>> c.recv()
|
|
|
|
|
'hello\n'
|
|
|
|
|
>>> r.send('hello\n')
|
|
|
|
|
>>> c.recvline()
|
|
|
|
|
'hello\n'
|
|
|
|
|
>>> r.sendline('hello')
|
|
|
|
|
>>> c.recv()
|
|
|
|
|
'hello\n'
|
|
|
|
|
>>> r.sendline('hello')
|
|
|
|
|
>>> c.recvline()
|
|
|
|
|
'hello\n'
|
|
|
|
|
>>> r.sendline('hello')
|
|
|
|
|
>>> c.recvline(keepends=False)
|
|
|
|
|
'hello'
|
|
|
|
|
>>> r.send('hello world')
|
|
|
|
|
>>> c.recvuntil('hello')
|
|
|
|
|
'hello'
|
|
|
|
|
>>> c.recv()
|
|
|
|
|
' world'
|
|
|
|
|
>>> c.close()
|
|
|
|
|
[*] Closed connection to 127.0.0.1 port 38684
|
|
|
|
|
>>> r.close()
|
|
|
|
|
[*] Closed connection to localhost port 46147
|
2017-10-11 17:22:24 +07:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
下面是一个与进程交互的例子:
|
2017-10-12 16:08:08 +07:00
|
|
|
|
```text
|
|
|
|
|
>>> p = process('/bin/sh')
|
2017-10-11 17:22:24 +07:00
|
|
|
|
[x] Starting local process '/bin/sh'
|
2017-10-12 16:08:08 +07:00
|
|
|
|
[+] Starting local process '/bin/sh': pid 26481
|
|
|
|
|
>>> p.sendline('sleep 3; echo hello world;')
|
|
|
|
|
>>> p.recvline(timeout=1)
|
|
|
|
|
'hello world\n'
|
|
|
|
|
>>> p.sendline('sleep 3; echo hello world;')
|
|
|
|
|
>>> p.recvline(timeout=1)
|
|
|
|
|
''
|
|
|
|
|
>>> p.recvline(timeout=5)
|
|
|
|
|
'hello world\n'
|
|
|
|
|
>>> p.interactive()
|
2017-10-11 17:22:24 +07:00
|
|
|
|
[*] Switching to interactive mode
|
|
|
|
|
whoami
|
|
|
|
|
firmy
|
|
|
|
|
^C[*] Interrupted
|
2017-10-12 16:08:08 +07:00
|
|
|
|
>>> p.close()
|
|
|
|
|
[*] Stopped process '/bin/sh' (pid 26481)
|
|
|
|
|
```
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
2017-10-12 16:08:08 +07:00
|
|
|
|
#### shellcraft
|
|
|
|
|
使用 shellcraft 模块可以生成对应架构和 shellcode 代码,直接使用链式调用的方法就可以得到,首先指定体系结构,再指定操作系统:
|
|
|
|
|
```
|
|
|
|
|
>>> print shellcraft.i386.nop().strip('\n')
|
|
|
|
|
nop
|
|
|
|
|
>>> print shellcraft.i386.linux.sh()
|
|
|
|
|
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
|
|
|
|
|
/* push '/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
|
2017-10-11 17:22:24 +07:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### asm
|
2017-10-12 16:08:08 +07:00
|
|
|
|
该模块用于汇编和反汇编代码。
|
|
|
|
|
|
|
|
|
|
体系结构,端序和字长需要在 `asm()` 和 `disasm()` 中设置,但为了避免重复,运行时变量最好使用 `pwnlib.context` 来设置。
|
|
|
|
|
|
|
|
|
|
汇编:(`pwnlib.asm.asm`)
|
|
|
|
|
```text
|
|
|
|
|
>>> asm('nop')
|
|
|
|
|
'\x90'
|
|
|
|
|
>>> asm(shellcraft.nop())
|
|
|
|
|
'\x90'
|
|
|
|
|
>>> asm('nop', arch='arm')
|
|
|
|
|
'\x00\xf0 \xe3'
|
|
|
|
|
>>> context.arch = 'arm'
|
|
|
|
|
>>> context.os = 'linux'
|
|
|
|
|
>>> context.endian = 'little'
|
|
|
|
|
>>> context.word_size = 32
|
|
|
|
|
>>> context
|
|
|
|
|
ContextType(arch = 'arm', bits = 32, endian = 'little', os = 'linux')
|
|
|
|
|
>>> asm('nop')
|
|
|
|
|
'\x00\xf0 \xe3'
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
>>> asm('mov eax, 1')
|
|
|
|
|
'\xb8\x01\x00\x00\x00'
|
|
|
|
|
>>> asm('mov eax, 1').encode('hex')
|
|
|
|
|
'b801000000'
|
|
|
|
|
```
|
|
|
|
|
请注意,这里我们生成了 i386 和 arm 两种不同体系结构的 `nop`,当你使用不同与本机平台的汇编时,需要安装该平台的 binutils,方法在上面已经介绍过了。
|
|
|
|
|
|
|
|
|
|
反汇编:(`pwnlib.asm.disasm`)
|
|
|
|
|
```text
|
|
|
|
|
>>> print disasm('\xb8\x01\x00\x00\x00')
|
|
|
|
|
0: b8 01 00 00 00 mov eax,0x1
|
|
|
|
|
>>> print disasm('6a0258cd80ebf9'.decode('hex'))
|
|
|
|
|
0: 6a 02 push 0x2
|
|
|
|
|
2: 58 pop eax
|
|
|
|
|
3: cd 80 int 0x80
|
|
|
|
|
5: eb f9 jmp 0x0
|
|
|
|
|
```
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
2017-10-12 16:08:08 +07:00
|
|
|
|
构建具有指定二进制数据的 ELF 文件:(`pwnlib.asm.make_elf`)
|
|
|
|
|
```text
|
|
|
|
|
>>> context.clear(arch='amd64')
|
|
|
|
|
>>> context
|
|
|
|
|
ContextType(arch = 'amd64', bits = 64, endian = 'little')
|
|
|
|
|
>>> bin_sh = asm(shellcraft.amd64.linux.sh())
|
|
|
|
|
>>> bin_sh
|
|
|
|
|
'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05'
|
|
|
|
|
>>> filename = make_elf(bin_sh, extract=False)
|
|
|
|
|
>>> filename
|
|
|
|
|
'/tmp/pwn-asm-V4GWGN/step3-elf'
|
|
|
|
|
>>> p = process(filename)
|
|
|
|
|
[x] Starting local process '/tmp/pwn-asm-V4GWGN/step3-elf'
|
|
|
|
|
[+] Starting local process '/tmp/pwn-asm-V4GWGN/step3-elf': pid 28323
|
|
|
|
|
>>> p.sendline('echo hello')
|
|
|
|
|
>>> p.recv()
|
|
|
|
|
'hello\n'
|
|
|
|
|
```
|
|
|
|
|
这里我们生成了 amd64,即 64 位 `/bin/sh` 的 shellcode,配合上 asm 函数,即可通过 `make_elf` 得到 ELF 文件。
|
|
|
|
|
|
|
|
|
|
另一个函数 `pwnlib.asm.make_elf_from_assembly` 允许你构建具有指定汇编代码的 ELF 文件:
|
|
|
|
|
```text
|
|
|
|
|
>>> asm_sh = shellcraft.amd64.linux.sh()
|
|
|
|
|
>>> print asm_sh
|
|
|
|
|
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
|
|
|
|
|
/* push '/bin///sh\x00' */
|
|
|
|
|
push 0x68
|
|
|
|
|
mov rax, 0x732f2f2f6e69622f
|
|
|
|
|
push rax
|
|
|
|
|
mov rdi, rsp
|
|
|
|
|
/* push argument array ['sh\x00'] */
|
|
|
|
|
/* push 'sh\x00' */
|
|
|
|
|
push 0x1010101 ^ 0x6873
|
|
|
|
|
xor dword ptr [rsp], 0x1010101
|
|
|
|
|
xor esi, esi /* 0 */
|
|
|
|
|
push rsi /* null terminate */
|
|
|
|
|
push 8
|
|
|
|
|
pop rsi
|
|
|
|
|
add rsi, rsp
|
|
|
|
|
push rsi /* 'sh\x00' */
|
|
|
|
|
mov rsi, rsp
|
|
|
|
|
xor edx, edx /* 0 */
|
|
|
|
|
/* call execve() */
|
|
|
|
|
push SYS_execve /* 0x3b */
|
|
|
|
|
pop rax
|
|
|
|
|
syscall
|
|
|
|
|
|
|
|
|
|
>>> filename = make_elf_from_assembly(asm_sh)
|
|
|
|
|
>>> filename
|
|
|
|
|
'/tmp/pwn-asm-ApZ4_p/step3'
|
|
|
|
|
>>> p = process(filename)
|
|
|
|
|
[x] Starting local process '/tmp/pwn-asm-ApZ4_p/step3'
|
|
|
|
|
[+] Starting local process '/tmp/pwn-asm-ApZ4_p/step3': pid 28429
|
|
|
|
|
>>> p.sendline('echo hello')
|
|
|
|
|
>>> p.recv()
|
|
|
|
|
'hello\n'
|
|
|
|
|
```
|
|
|
|
|
与上一个函数不同的是,`make_elf_from_assembly` 直接从汇编生成 ELF 文件,并且保留了所有的符号,例如标签和局部变量等。
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
#### elf
|
2017-10-12 16:08:08 +07:00
|
|
|
|
该模块用于 ELF 二进制文件的操作,包括符号查找、虚拟内存、文件偏移,以及修改和保存二进制文件等功能。(`pwnlib.elf.elf.ELF`)
|
|
|
|
|
```text
|
|
|
|
|
>>> e = ELF('/bin/cat')
|
|
|
|
|
[*] '/bin/cat'
|
|
|
|
|
Arch: amd64-64-little
|
|
|
|
|
RELRO: Full RELRO
|
|
|
|
|
Stack: Canary found
|
|
|
|
|
NX: NX enabled
|
|
|
|
|
PIE: PIE enabled
|
|
|
|
|
>>> print hex(e.address)
|
|
|
|
|
0x400000
|
|
|
|
|
>>> print hex(e.symbols['write'])
|
|
|
|
|
0x401680
|
|
|
|
|
>>> print hex(e.got['write'])
|
|
|
|
|
0x60b070
|
|
|
|
|
>>> print hex(e.plt['write'])
|
|
|
|
|
0x401680
|
|
|
|
|
```
|
|
|
|
|
上面的代码分别获得了 ELF 文件装载的基地址、函数地址、GOT 表地址和 PLT 表地址。
|
|
|
|
|
|
|
|
|
|
我们常常用它打开一个 libc.so,从而得到 system 函数的位置,这在 CTF 中是非常有用的:
|
|
|
|
|
```text
|
|
|
|
|
>>> e = ELF('/usr/lib/libc.so.6')
|
|
|
|
|
[*] '/usr/lib/libc.so.6'
|
|
|
|
|
Arch: amd64-64-little
|
|
|
|
|
RELRO: Full RELRO
|
|
|
|
|
Stack: Canary found
|
|
|
|
|
NX: NX enabled
|
|
|
|
|
PIE: PIE enabled
|
|
|
|
|
>>> print hex(e.symbols['system'])
|
|
|
|
|
0x42010
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
我们甚至可以修改 ELF 文件的代码:
|
|
|
|
|
```text
|
|
|
|
|
>>> e = ELF('/bin/cat')
|
|
|
|
|
>>> e.read(e.address+1, 3)
|
|
|
|
|
'ELF'
|
|
|
|
|
>>> e.asm(e.address, 'ret')
|
|
|
|
|
>>> e.save('/tmp/quiet-cat')
|
|
|
|
|
>>> disasm(file('/tmp/quiet-cat','rb').read(1))
|
|
|
|
|
' 0: c3 ret'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
下面是一些常用函数:
|
|
|
|
|
- `asm(address, assembly)`:汇编指定指令并插入到 ELF 的指定地址处,需要使用 ELF.save() 保存
|
|
|
|
|
- `bss(offset)`:返回 `.bss` 段加上 `offset` 后的地址
|
|
|
|
|
- `checksec()`:打印出文件使用的安全保护
|
|
|
|
|
- `disable_nx()`:关闭 NX
|
|
|
|
|
- `disasm(address, n_bytes)`:返回对指定虚拟地址进行反汇编后的字符串
|
|
|
|
|
- `offset_to_vaddr(offset)`:将指定偏移转换为虚拟地址
|
|
|
|
|
- `vaddr_to_offset(address)`:将指定虚拟地址转换为文件偏移
|
|
|
|
|
- `read(address, count)`:从指定虚拟地址读取 `count` 个字节的数据
|
|
|
|
|
- `write(address, data)`:在指定虚拟地址处写入 `data`
|
|
|
|
|
- `section(name)`:获取 `name` 段的数据
|
|
|
|
|
- `debug()`:使用 `gdb.debug()` 进行调试
|
|
|
|
|
|
|
|
|
|
最后还要注意一下 `pwnlib.elf.corefile`,它用于处理核心转储文件(Core Dump),当我们在写利用代码时,核心转储文件是非常有用的,关于它更详细的内容已经在前面 Linux基础一章中讲过,这里我们还是使用那一章中的示例代码,但使用 pwntools 来操作。
|
|
|
|
|
```
|
|
|
|
|
>>> core = Corefile('/tmp/core-a.out-30555-1507796886')
|
|
|
|
|
[x] Parsing corefile...
|
|
|
|
|
[*] '/tmp/core-a.out-30555-1507796886'
|
|
|
|
|
Arch: i386-32-little
|
|
|
|
|
EIP: 0x565cd57b
|
|
|
|
|
ESP: 0x4141413d
|
|
|
|
|
Exe: '/home/firmy/a.out' (0x565cd000)
|
|
|
|
|
Fault: 0x4141413d
|
|
|
|
|
[+] Parsing corefile...: Done
|
|
|
|
|
>>> core.registers
|
|
|
|
|
{'xds': 43, 'eip': 1448924539, 'xss': 43, 'esp': 1094795581, 'xgs': 99, 'edi': 0, 'orig_eax': 4294967295, 'xcs': 35, 'eax': 1, 'ebp': 1094795585, 'xes': 43, 'eflags': 66182, 'edx': 4151195744, 'ebx': 1094795585, 'xfs': 0, 'esi': 4151189032, 'ecx': 1094795585}
|
|
|
|
|
>>> print core.maps
|
|
|
|
|
565cd000-565ce000 r-xp 1000 /home/firmy/a.out
|
|
|
|
|
565ce000-565cf000 r--p 1000 /home/firmy/a.out
|
|
|
|
|
565cf000-565d0000 rw-p 1000 /home/firmy/a.out
|
|
|
|
|
57b3c000-57b5e000 rw-p 22000
|
|
|
|
|
f7510000-f76df000 r-xp 1cf000 /usr/lib32/libc-2.26.so
|
|
|
|
|
f76df000-f76e0000 ---p 1000 /usr/lib32/libc-2.26.so
|
|
|
|
|
f76e0000-f76e2000 r--p 2000 /usr/lib32/libc-2.26.so
|
|
|
|
|
f76e2000-f76e3000 rw-p 1000 /usr/lib32/libc-2.26.so
|
|
|
|
|
f76e3000-f76e6000 rw-p 3000
|
|
|
|
|
f7722000-f7724000 rw-p 2000
|
|
|
|
|
f7724000-f7726000 r--p 2000 [vvar]
|
|
|
|
|
f7726000-f7728000 r-xp 2000 [vdso]
|
|
|
|
|
f7728000-f774d000 r-xp 25000 /usr/lib32/ld-2.26.so
|
|
|
|
|
f774d000-f774e000 r--p 1000 /usr/lib32/ld-2.26.so
|
|
|
|
|
f774e000-f774f000 rw-p 1000 /usr/lib32/ld-2.26.so
|
|
|
|
|
ffe37000-ffe58000 rw-p 21000 [stack]
|
|
|
|
|
>>> print hex(core.fault_addr)
|
|
|
|
|
0x4141413d
|
|
|
|
|
>>> print hex(core.pc)
|
|
|
|
|
0x565cd57b
|
|
|
|
|
>>> print core.libc
|
|
|
|
|
f7510000-f76df000 r-xp 1cf000 /usr/lib32/libc-2.26.so
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### dynelf
|
|
|
|
|
`pwnlib.dynelf.DynELF`
|
|
|
|
|
|
|
|
|
|
该模块是专门用来应对无 libc 情况下的漏洞利用。它首先找到 glibc 的基地址,然后使用符号表和字符串表对所有符号进行解析,直到找到我们需要的函数的符号。这是一个有趣的话题,我们会专门开一个章节去讲解它。详见 *4.4 使用 DynELF 泄露函数地址*
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
#### fmtstr
|
2017-10-12 16:08:08 +07:00
|
|
|
|
`pwnlib.fmtstr.FmtStr`,`pwnlib.fmtstr.fmtstr_payload`
|
|
|
|
|
|
|
|
|
|
该模块用于格式化字符串漏洞的利用,格式化字符串漏洞是 CTF 中一种常见的题型,我们会在后面的章节中详细讲述,关于该模块的使用也会留到那儿。详见 *3.3.1 格式化字符串漏洞*
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
#### gdb
|
2017-10-14 19:17:42 +07:00
|
|
|
|
`pwnlib.gdb`
|
|
|
|
|
|
|
|
|
|
在写漏洞利用的时候,常常需要使用 gdb 动态调试,该模块就提供了这方面的支持。
|
|
|
|
|
|
|
|
|
|
两个常用函数:
|
|
|
|
|
- `gdb.attach(target, gdbscript=None)`:在一个新终端打开 gdb 并 attach 到指定 PID 的进程,或是一个 `pwnlib.tubes` 对象。
|
|
|
|
|
- `gdb.debug(args, gdbscript=None)`:在新终端中使用 gdb 加载一个二进制文件。
|
|
|
|
|
|
|
|
|
|
上面两种方法都可以在开启的时候传递一个脚本到 gdb,可以很方便地做一些操作,如自动设置断点。
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# attach to pid 1234
|
|
|
|
|
gdb.attach(1234)
|
|
|
|
|
|
|
|
|
|
# attach to a process
|
|
|
|
|
bash = process('bash')
|
|
|
|
|
gdb.attach(bash, '''
|
|
|
|
|
set follow-fork-mode child
|
|
|
|
|
continue
|
|
|
|
|
''')
|
|
|
|
|
bash.sendline('whoami')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
# Create a new process, and stop it at 'main'
|
|
|
|
|
io = gdb.debug('bash', '''
|
|
|
|
|
# Wait until we hit the main executable's entry point
|
|
|
|
|
break _start
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Now set breakpoint on shared library routines
|
|
|
|
|
break malloc
|
|
|
|
|
break free
|
|
|
|
|
continue
|
|
|
|
|
''')
|
|
|
|
|
```
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
#### memleak
|
2017-10-14 19:17:42 +07:00
|
|
|
|
`pwnlib.memleak`
|
|
|
|
|
|
|
|
|
|
该模块用于内存泄露的利用。可用作装饰器。它会将泄露的内存缓存起来,在漏洞利用过程中可能会用到。
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
2017-10-12 16:08:08 +07:00
|
|
|
|
#### rop
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
#### util
|
2017-10-15 13:44:32 +07:00
|
|
|
|
`pwnlib.util.packing`, `pwnlib.util.cyclic`
|
|
|
|
|
|
|
|
|
|
util 其实是一些模块的集合,包含了一些实用的小工具。这里主要介绍两个,packing 和 cyclic。
|
|
|
|
|
|
|
|
|
|
packing 模块用于将整数打包和解包,它简化了标准库中的 `struct.pack` 和 `struct.unpack` 函数,同时增加了对任意宽度整数的支持。
|
|
|
|
|
|
|
|
|
|
使用 `p32`, `p64`, `u32`, `u64` 函数分别对 32 位和 64 位整数打包和解包,也可以使用 `pack()` 自己定义长度,另外添加参数 `endian` 和 `signed` 设置端序和是否带符号。
|
|
|
|
|
```
|
|
|
|
|
>>> p32(0xdeadbeef)
|
|
|
|
|
'\xef\xbe\xad\xde'
|
|
|
|
|
>>> p64(0xdeadbeef).encode('hex')
|
|
|
|
|
'efbeadde00000000'
|
|
|
|
|
>>> p32(0xdeadbeef, endian='big', sign='unsigned')
|
|
|
|
|
'\xde\xad\xbe\xef'
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
>>> u32('1234')
|
|
|
|
|
875770417
|
|
|
|
|
>>> u32('1234', endian='big', sign='signed')
|
|
|
|
|
825373492
|
|
|
|
|
>>> u32('\xef\xbe\xad\xde')
|
|
|
|
|
3735928559
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
cyclic 模块在缓冲区溢出中很有用,它帮助生成模式字符串,然后查找偏移,以确定返回地址。
|
|
|
|
|
```
|
|
|
|
|
>>> cyclic(20)
|
|
|
|
|
'aaaabaaacaaadaaaeaaa'
|
|
|
|
|
>>> cyclic_find(0x61616162)
|
|
|
|
|
4
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
## Pwntools 在 CTF 中的运用
|
2017-10-15 13:44:32 +07:00
|
|
|
|
可以在下面的仓库中找到大量使用 pwntools 的 write-up:
|
|
|
|
|
[pwntools-write-ups](https://github.com/Gallopsled/pwntools-write-ups)
|
|
|
|
|
|
2017-10-11 17:22:24 +07:00
|
|
|
|
|
|
|
|
|
## 参考资料
|
|
|
|
|
- [docs.pwntools.com](https://docs.pwntools.com/en/stable/index.html)
|