update pwntools

This commit is contained in:
firmianay 2017-10-12 17:08:08 +08:00
parent 058c9c5e70
commit da67b9a3e4
2 changed files with 347 additions and 78 deletions

View File

@ -40,12 +40,47 @@ $ yaourt -S python2-pwntools
$ yaourt -S python2-pwntools-git $ yaourt -S python2-pwntools-git
``` ```
但是由于 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
```
测试安装是否成功: 测试安装是否成功:
```python ```python
>>> import pwn >>> from pwn import *
>>> pwn.asm("xor eax,eax") >>> asm('nop')
'1\xc0' '\x90'
>>> asm('nop', arch='arm')
'\x00\xf0 \xe3'
``` ```
@ -68,7 +103,7 @@ Pwntools 分为两个模块,一个是 `pwn`,简单地使用 `from pwn import
- `libcdb`libc 数据库 - `libcdb`libc 数据库
- `log`:日志记录 - `log`:日志记录
- **`memleak`**:用于内存泄露 - **`memleak`**:用于内存泄露
- `rop`ROP 利用模块,包括 rop 和 srop - **`rop`**ROP 利用模块,包括 rop 和 srop
- `runner`:运行 shellcode - `runner`:运行 shellcode
- **`shellcraft`**shellcode 生成器 - **`shellcraft`**shellcode 生成器
- `term`:终端处理 - `term`:终端处理
@ -107,106 +142,310 @@ Pwntools 分为两个模块,一个是 `pwn`,简单地使用 `from pwn import
- `close()`:关闭管道 - `close()`:关闭管道
下面是一个例子,先使用 listen 开启一个本地的监听端口,然后使用 remote 开启一个套接字管道与之交互: 下面是一个例子,先使用 listen 开启一个本地的监听端口,然后使用 remote 开启一个套接字管道与之交互:
``` ```text
In [1]: from pwn import * >>> from pwn import *
>>> l = listen()
In [2]: l = listen()
[x] Trying to bind to 0.0.0.0 on port 0 [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 [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 [+] Trying to bind to 0.0.0.0 on port 0: Done
[x] Waiting for connections on 0.0.0.0:35117 [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
In [3]: r = remote('localhost', l.lport) >>> c = l.wait_for_connection()
[x] Opening connection to localhost on port 35117 >>> r.send('hello\n')
[x] Opening connection to localhost on port 35117: Trying ::1 >>> c.recv()
[x] Opening connection to localhost on port 35117: Trying 127.0.0.1 'hello\n'
[+] Opening connection to localhost on port 35117: Done >>> r.send('hello\n')
>>> c.recvline()
[+] Waiting for connections on 0.0.0.0:35117: Got connection from 127.0.0.1 on port 36966 'hello\n'
In [4]: c = l.wait_for_connection() >>> r.sendline('hello')
>>> c.recv()
In [5]: r.send('hello\n') 'hello\n'
>>> r.sendline('hello')
In [6]: c.recv() >>> c.recvline()
Out[6]: 'hello\n' 'hello\n'
>>> r.sendline('hello')
In [7]: r.send('hello\n') >>> c.recvline(keepends=False)
'hello'
In [8]: c.recvline() >>> r.send('hello world')
Out[8]: 'hello\n' >>> c.recvuntil('hello')
'hello'
In [9]: r.sendline('hello') >>> c.recv()
' world'
In [10]: c.recv() >>> c.close()
Out[10]: 'hello\n' [*] Closed connection to 127.0.0.1 port 38684
>>> r.close()
In [11]: r.sendline('hello') [*] Closed connection to localhost port 46147
In [12]: c.recvline()
Out[12]: 'hello\n'
In [13]: r.sendline('hello')
In [14]: c.recvline(keepends=False)
Out[14]: 'hello'
In [15]: r.send('hello world')
In [16]: c.recvuntil('hello')
Out[16]: 'hello'
In [17]: c.recv()
Out[17]: ' world'
In [18]: c.close()
[*] Closed connection to 127.0.0.1 port 36966
In [19]: r.close()
[*] Closed connection to localhost port 35117
``` ```
下面是一个与进程交互的例子: 下面是一个与进程交互的例子:
``` ```text
In [1]: p = process('/bin/sh') >>> p = process('/bin/sh')
[x] Starting local process '/bin/sh' [x] Starting local process '/bin/sh'
[+] Starting local process '/bin/sh': pid 32165 [+] Starting local process '/bin/sh': pid 26481
>>> p.sendline('sleep 3; echo hello world;')
In [2]: p.sendline('sleep 3; echo hello world;') >>> p.recvline(timeout=1)
'hello world\n'
In [3]: p.recvline(timeout=1) >>> p.sendline('sleep 3; echo hello world;')
Out[3]: 'hello world\n' >>> p.recvline(timeout=1)
''
In [4]: p.sendline('sleep 3; echo hello world;') >>> p.recvline(timeout=5)
'hello world\n'
In [5]: p.recvline(timeout=1) >>> p.interactive()
Out[5]: ''
In [6]: p.recvline(timeout=5)
Out[6]: 'hello world\n'
In [7]: p.interactive()
[*] Switching to interactive mode [*] Switching to interactive mode
whoami whoami
firmy firmy
^C[*] Interrupted ^C[*] Interrupted
>>> p.close()
[*] Stopped process '/bin/sh' (pid 26481)
```
In [8]: p.close() #### shellcraft
[*] Stopped process '/bin/sh' (pid 32165) 使用 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
``` ```
#### asm #### asm
该模块用于汇编和反汇编代码。
#### dynelf 体系结构,端序和字长需要在 `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
```
构建具有指定二进制数据的 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 文件,并且保留了所有的符号,例如标签和局部变量等。
#### elf #### elf
该模块用于 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 泄露函数地址*
#### fmtstr #### fmtstr
`pwnlib.fmtstr.FmtStr``pwnlib.fmtstr.fmtstr_payload`
该模块用于格式化字符串漏洞的利用,格式化字符串漏洞是 CTF 中一种常见的题型,我们会在后面的章节中详细讲述,关于该模块的使用也会留到那儿。详见 *3.3.1 格式化字符串漏洞*
#### gdb #### gdb
#### memleak #### memleak
#### shellcraft #### rop
#### util #### util

View File

@ -0,0 +1,30 @@
#!/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