Skip to main content

pwn.college | pwntools tutorials

·1172 words·6 mins
WiZee
Author
WiZee
Infosec student @ ESNA
Table of Contents

Pwntools is a toolkit (including various handy tools) and a software library designed to simplify the process of exploitation in CTF competitions as much as possible, while also enhancing the readability of the exploit code.

  • send, recv
  • pack, unpack
  • Assembly & Disassembly
  • ELF parsing
  • cyclic
  • ShellCode & ROP

You can use the following python code to import the pwntools library:

from pwn import *

Lectures and Reading


Level 0.0 (1/12)
#

This level is a tutorial and relatively simple. You can directly run /challenge/pwntools-tutorials-level0.0 in the terminal and then input a specific string (which you can find by reading the bypass_me function), but that is not the goal of this level.

This level will guide you on how to use pwntools to complete the challenge. Next, you need to use the process, send, recv, and other APIs in pwntools to write an exploit script, send a specific input to bypass the check, and read the /flag. Please refer to the following pwntools example code (hint: be sure to replace FIXME with the specific string mentioned above):

Source code
#

pwntools-tutorials-level0.0.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include"util.h"


void print_flag()
{
	char *p;
	FILE *fp;
	char flag[100];

	fp = fopen("/flag", "r");

	if (!fp) {
		perror("[-] fopen failed");
	}

	p = fgets(flag, sizeof(flag), fp);
	if (!p) {
		perror("[-] fgets failed");
		fclose(fp);
	}
	
	printf("%s", flag);

	fclose(fp);
}

int bypass_me(char *buf)
{	
	if (!strncmp(buf, "pokemon", 7)) {
		return 1;
	}
	
	return 0;
}

int main()
{
	char buffer[100];

	print_desc();

	fgets(buffer, sizeof(buffer), stdin);

	if (bypass_me(buffer)) {
		print_flag();
	} else {
		printf("You need to bypass some conditions to get the flag: \n");
		printf("Please refer to the source code to understand these conditions\n");	
	}

	print_exit();
	return 0;
}

Analysis
#

The function bypass_me compares the first 7 characters of the user input with the string pokemon. To trigger the condition and call print_flag(), user input must start with pokemon. Any additional characters such as a newline at the end from fgets won’t affect the check, as only the first 7 characters are compared.

Solve
#

solve.py

from pwn import *

# Set architecture, os and log level
context(arch="amd64", os="linux", log_level="info")

# Load the ELF file and execute it as a new process.
challenge_path = "/challenge/pwntools-tutorials-level0.0"
p = process(challenge_path)

payload = b'pokemon\n'
# Send the payload after the string ":)\n###\n" is found.
p.sendafter(":)\n###\n", payload)

# Another way of doing things would be:
#payload = b'pokemon'
#p.sendlineafter(":)\n###\n", payload)

# Receive flag from the process
flag = p.recvline()
print(f"flag is: {flag}")
python3 solve.py
flag is: b'pwn.college{QH6_CnU1eYVYzIZaqVSlGcXu7e0.REDACTED}\n'

Flag
#

pwn.college{QH6_CnU1eYVYzIZaqVSlGcXu7e0.REDACTED}


Level 1.0 (2/12)
#

This level requires you to read the bypass_me function in the challenge and use pwntools to complete the challenge. Next, you need to use the p64, p32, p16, p8, and other APIs in pwntools to write an exploit script, send a specific input to bypass the check, and read the /flag.

Source code
#

pwntools-tutorials-level1.0.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include"util.h"


void print_flag()
{
	char *p;
	FILE *fp;
	char flag[100];

	fp = fopen("/flag", "r");

	if (!fp) {
		perror("[-] fopen failed");
	}

	p = fgets(flag, sizeof(flag), fp);
	if (!p) {
		perror("[-] fgets failed");
		fclose(fp);
	}
	
	printf("%s", flag);

	fclose(fp);
}

int bypass_me(char *buf)
{
	unsigned int magic = 0xdeadbeef;
	
	if (!strncmp(buf, (char *)&magic, 4)) {
		return 1;
	}
	
	return 0;
}

int main()
{
	char buffer[100];

	print_desc();

	fgets(buffer, sizeof(buffer), stdin);

	if (bypass_me(buffer)) {
		print_flag();
	} else {
		printf("You need to bypass some conditions to get the flag: \n");
		printf("Please refer to the source code to understand these conditions\n");	
	}

	print_exit();
	return 0;
}

Analysis
#

The program reads a line of input from the user and then checks whether the first 4 bytes match the magic number 0xdeadbeef. Specifically, the function bypass_me uses strncmp to compare the user-supplied bytes to the in-memory representation of 0xdeadbeef. If they match (i.e. if the first 4 bytes are exactly those of the magic value), bypass_me returns true, which causes the program to call print_flag() and display the flag from the /flag file.

On a little-endian system, you need to provide the byte sequence \xef\xbe\xad\xde as your input to meet this condition.

Solve
#

solve.py

from pwn import *

# Set architecture, os and log level
context(arch="amd64", os="linux", log_level="info")

# Load the ELF file and execute it as a new process.
challenge_path = "/challenge/pwntools-tutorials-level1.0"
p = process(challenge_path)

# Send 0xdeadbeef as byte sequence after the string ":)\n###\n" is found.
payload = b'\xef\xbe\xad\xde'
p.sendlineafter(":)\n###\n", payload)

# Receive flag from the process
flag = p.recvline()
print(f"flag is: {flag}")
python3 solve.py
flag is: b'pwn.college{gSySuIDEHt5DaIxAZ_5cUgu1SBV.REDACTED}\n'

Flag
#

pwn.college{gSySuIDEHt5DaIxAZ_5cUgu1SBV.REDACTED}


Level 1.1 (3/12)
#

This level requires you to read the bypass_me function in the challenge and use pwntools to complete the challenge. You need to use Python string concatenation and the p64, p32, p16, p8 APIs from pwntools to write an exploit script, send a specific input to bypass the check, and read the /flag.

Source code
#

pwntools-tutorials-level1.1.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include"util.h"


void print_flag()
{
	char *p;
	FILE *fp;
	char flag[100];

	fp = fopen("/flag", "r");

	if (!fp) {
		perror("[-] fopen failed");
	}

	p = fgets(flag, sizeof(flag), fp);
	if (!p) {
		perror("[-] fgets failed");
		fclose(fp);
	}
	
	printf("%s\n", flag);

	fclose(fp);
}

int bypass_me(char *buf)
{
	int flag = 1;
	int num;
	
	if (buf[0] != 'p' || buf[1] != 0x15) {
		flag = 0;
		goto out; 
	}

	memcpy(&num, buf + 2, 4);
	if (num != 123456789) {
		flag = 0;
		goto out;
	}

	if (strncmp(buf + 6, "Bypass Me:)", 11)) {
		flag = 0;
		goto out;
	}
	
out:
	return flag;
}

int main()
{
	char buffer[100];

	print_desc();

	fgets(buffer, sizeof(buffer), stdin);

	if (bypass_me(buffer)) {
		print_flag();
	} else {
		printf("You need to bypass some conditions to get the flag: \n");
		printf("Please refer to the source code to understand these conditions\n");	
	}

	print_exit();
	return 0;
}

Analysis
#

The function checks three conditions on the input buffer:

  1. First two bytes:

    • buf[0] must be the character p.
    • buf[1] must equal 0x15.
  2. Next four bytes:

    • Bytes at positions 2–5 are copied into an integer. They must represent the number 123456789.
    • On a little-endian system, this means the bytes should be 0x15, 0xCD, 0x5B, 0x07.
  3. Following eleven bytes:

    • Starting at buf[6], the next 11 characters must exactly match the string Bypass Me:).

If all these conditions are met, bypass_me return true and trigger print_flag().

Solve
#

solve.py

from pwn import *

# Set architecture, os and log level
context(arch="amd64", os="linux", log_level="info")

# Load the ELF file and execute it as a new process.
challenge_path = "/challenge/pwntools-tutorials-level1.1"
p = process(challenge_path)

payload = b'p\x15\x15\xCD\x5B\x07Bypass Me:)'
# Send the payload after the string ":)\n###\n" is found.
p.sendlineafter(":)\n###\n", payload)

# Receive flag from the process
flag = p.recvline()
print(f"flag is: {flag}")
python3 solve.py
flag is: b'pwn.college{EkF7lJR3MyL7uSvu1WnT0kHvJIo.REDACTED}\n'

Flag
#

pwn.college{EkF7lJR3MyL7uSvu1WnT0kHvJIo.REDACTED}


Level 2.0 (4/12)
#

TODO.


Level 2.1 (5/12)
#

TODO.


Level 2.2 (6/12)
#

TODO.


Level 2.3 (7/12)
#

TODO.


Level 2.4 (8/12)
#

TODO.


Level 2.5 (9/12)
#

TODO.


Level 2.6 (10/12)
#

TODO.


Level 3.0 (11/12)
#

TODO.


Level 4.0 (12/12)
#

TODO.

Reply by Email