|
|
|
Pwntools is a Python library made for CTF and the rapid development of security exploitation of all kinds. It comes in two flavors, `pwn` and `pwnlib`. "pwn" is meant to be used wholesale with `import pwn` or `from pwn import *`, and brings with it numerous side-effects that could interfere with or augment the normal functions of your code. `pwnlib` is the clean version that can be used without changing how Python works, and is meant to be imported piecewise depending on what modules are needed.
|
|
|
|
|
|
|
|
## 1. Making Connections
|
|
|
|
|
|
|
|
The first step in causing mischief is talking to people. Making connections is made easy with the `pwnlib.tubes` module.
|
|
|
|
|
|
|
|
```python
|
|
|
|
from pwnlib import *
|
|
|
|
```
|
|
|
|
Remote connections are made with the `pwnlib.tubes.remote` module.
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> conn = remote('ftp.ubuntu.com',21)
|
|
|
|
>>> conn.recvline()
|
|
|
|
'220 ...'
|
|
|
|
>>> conn.send('USER anonymous\r\n')
|
|
|
|
>>> conn.recvuntil(' ', drop=True)
|
|
|
|
'331'
|
|
|
|
>>> conn.recvline()
|
|
|
|
'Please specify the password.\r\n'
|
|
|
|
>>> conn.close()
|
|
|
|
```
|
|
|
|
|
|
|
|
To set up a listenener:
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> l = listen()
|
|
|
|
>>> r = remote('localhost', l.lport)
|
|
|
|
>>> c = l.wait_for_connection()
|
|
|
|
>>> r.send('hello')
|
|
|
|
>>> c.recv()
|
|
|
|
'hello'
|
|
|
|
```
|
|
|
|
|
|
|
|
Interacting with processes is done with `pwntools.tubes.process`...
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> sh = process('/bin/sh')
|
|
|
|
>>> sh.sendline('sleep 3; echo hello world;')
|
|
|
|
>>> sh.recvline(timeout=1)
|
|
|
|
''
|
|
|
|
>>> sh.recvline(timeout=5)
|
|
|
|
'hello world\n'
|
|
|
|
>>> sh.close()
|
|
|
|
```
|
|
|
|
|
|
|
|
...and can be done truly fully interactively
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> sh.interactive()
|
|
|
|
$ whoami
|
|
|
|
user
|
|
|
|
```
|
|
|
|
|
|
|
|
## 2. Packing Integers
|
|
|
|
|
|
|
|
When writing exploits, one might find themselves in need of a way to convert between Python integers are physical sequences of bytes. This is done with the `pwntools.util.packing` module.
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> import struct
|
|
|
|
>>> p32(0xdeadbeef) == struct.pack('I', 0xdeadbeef)
|
|
|
|
True
|
|
|
|
>>> leet = '37130000'.decode('hex')
|
|
|
|
>>> u32('abcd') == struct.unpack('I', 'abcd')[0]
|
|
|
|
True
|
|
|
|
```
|
|
|
|
|
|
|
|
## 3. Target architecture
|
|
|
|
|
|
|
|
For better or for worse, not every device is the exact same. The target architecture can be passed as an argument to many routines within `pwntools`.
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> asm('nop')
|
|
|
|
'\x90'
|
|
|
|
>>> asm('nop', arch='arm')
|
|
|
|
'\x00\xf0 \xe3'
|
|
|
|
```
|
|
|
|
|
|
|
|
Operating system, archicture, word size, and endianess can also be set globally.
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> context.arch = 'i386'
|
|
|
|
>>> context.os = 'linux'
|
|
|
|
>>> context.endian = 'little'
|
|
|
|
>>> context.word_size = 32
|
|
|
|
```
|
|
|
|
|
|
|
|
Or, they can all be set with `context(arch='arm',os='linux',endian='big',word_size=32)`
|
|
|
|
|
|
|
|
## 4. Assembly and Disassembly
|
|
|
|
|
|
|
|
The `pwnlib.asm` module can be used for all needs pertaining to Assembly, encoding or decoding between hex and asm, and outputting in a readable format.
|
|
|
|
|
|
|
|
Encoding is as simple as
|
|
|
|
```python
|
|
|
|
>>> asm('mov eax, 0').encode('hex')
|
|
|
|
'b800000000'
|
|
|
|
```
|
|
|
|
|
|
|
|
Should you have to deal with preassembled code, there is no need to worry.
|
|
|
|
|
|
|
|
```python
|
|
|
|
>>> print disasm('6a0258cd80ebf9'.decode('hex'))
|
|
|
|
0: 6a 02 push 0x2
|
|
|
|
2: 58 pop eax
|
|
|
|
3: cd 80 int 0x80
|
|
|
|
5: eb f9 jmp 0x0
|
|
|
|
``` |
|
|
|
\ No newline at end of file |