This is a very brief cheatsheet and introduction to pwntools for CTFs. I am writing this specifically for Sieberrsec CTF 5.0, but it can be applied for all CTFs.


Install and Import

pip install pwntools: to install pwntools, use this command in the terminal

from pwn import *: put this at the top of your python file to import pwntools.


Making connections

r = remote(HOST, PORT): connects to a remote connection, with the relevant host and port

  • Eg if the CTF tells you to connect to the instance via nc 1.1.1.1 2000, 1.1.1.1 is the host, and 2000 is the port
  • This command is in effect the same as nc

r.interactive(): allows you to interact with the process yourself

  • We usually use this at the very end after we have obtained the shell or when the flag is printed out

ELF manipulation

CTF challenges usually provide an ELF file for you to run. An ELF file is essentially the linux version of an .exe file.


elf = ELF('./NAME'): this allows you to get information about an ELF file of the specified name

r = elf.process(stdin=PTY): this executes the ELF file

  • Usually we use this to test the challenge locally
  • Then switch to remote(HOST,PORT) to connect to the remote instance

elf.symbols['FUNCTION']: gets the address of the specified function

  • Eg elf.symbols['main'] gives the address of main()

Reading data and providing input

conn.recvline(): reads one line of data

conn.recvlines(N): reads N lines of data

conn.recvuntil(VALUE): reads data until VALUE

conn.sendline(VALUE): sends an input of VALUE


Further readings

These commands are left as an exercise to the reader to learn more about. For more commands, refer to their documentation.

p64(NUMBER) and p32(NUMBER): struct unpacking

elf.got['write'],elf.plt['write']: get the GOT and PLT addresses


Sample

Imagine that you are provided with a file which does this:

int main() {
    int n1 = rand();
    int input;

    // prints "n1: xx" where xx is the number randomly generated
    printf("n1: %d\n",n1);

    puts("What is the value of n1?");

    // gets an integer input
    scanf("d",&input);

    // get the flag if input is equal to n1
    if (input == n1) {
        print_flag();
    }
}

So, the file will print the value of n1, and then you will need to input n1 back.

This is how you can use pwntools to solve the challenge:

from pwn import *

elf = context.binary = ELF("./challenge")
local = False

if local:
    # execute the local file
    r = elf.process()
else:
    # connect to the remote instance
    r = remote("challs.sieberr.live", 1337)

# get the value of n1
r.recvuntil(b'n1: ')
n1 = int(r.recvline())

# input n1
r.sendline(str(n1))

# interact with the process yourself, so you can see the value of the flag
r.interactive()