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

OverTheWire Maze Level 2 → 3 tutorial!!

Login

Use the maze2 account from the previous level.

ssh maze2@maze.labs.overthewire.org -p <PORT>
# password: beinguthok

Binary for this challenge: /maze/maze3


Task

Reverse and exploit maze3 to escalate from maze2 to maze3 (read /etc/maze_pass/maze3).


A little bit of Theory

This binary is a cute sample of self-modifying code:

  • There are three routines: fine, l1, and d1.
  • fine sets up registers, points ESI/EDI at the bytes of d1, sets ECX = 0x2C (44), and loads EDX = 0x12345678 (our XOR key).
  • l1 is a tiny loop: lods (load a dword from [esi]), xor eax, edx, stos (store into [edi]), loop. → This XOR-decodes 44 bytes of d1 in place.
  • After decoding, d1 becomes a different function that:

    1. pops eax (pointer to argv[1]),
    2. checks *(uint32_t*)argv[1] == 0x1337c0de,
    3. if true, executes /bin//sh; else exit(0).

So the whole “puzzle” is supplying little-endian bytes that equal 0x1337c0de.


Walkthrough (with GDB peeks)

Below are the actual disassemblies observed:

fine

0x08048096 <+0>:  pop    eax
0x08048097 <+1>:  mov    eax,0x7d
0x0804809c <+6>:  mov    ebx,0x8048060
0x080480a1 <+11>: and    ebx,0xfffff000
0x080480a7 <+17>: mov    ecx,0x97
0x080480ac <+22>: mov    edx,0x7
0x080480b1 <+27>: int    0x80
0x080480b3 <+29>: lea    esi,[0x80480cb]
0x080480b9 <+35>: mov    edi,esi
0x080480bb <+37>: mov    ecx,0x2c
0x080480c0 <+42>: mov    edx,0x12345678

l1

0x080480c5 <+0>:  lods   eax,DWORD PTR ds:[esi]
0x080480c6 <+1>:  xor    eax,edx
0x080480c8 <+3>:  stos   DWORD PTR es:[edi],eax
0x080480c9 <+4>:  loop   0x80480c5 <l1>

d1 (after decode)

0x080480cb <+0>:  pop    eax
0x080480cc <+1>:  cmp    DWORD PTR [eax],0x1337c0de
0x080480d2 <+7>:  jne    0x80480ed <d1+34>
0x080480d4 <+9>:  xor    eax,eax
0x080480d6 <+11>: push   eax
0x080480d7 <+12>: push   0x68732f2f
0x080480dc <+17>: push   0x6e69622f
0x080480e1 <+22>: mov    ebx,esp
0x080480e3 <+24>: push   eax
0x080480e4 <+25>: push   ebx
0x080480e5 <+26>: mov    ecx,esp
0x080480e7 <+28>: xor    edx,edx
0x080480e9 <+30>: mov    al,0xb
0x080480eb <+32>: int    0x80
0x080480ed <+34>: mov    eax,0x1
0x080480f2 <+39>: xor    ebx,ebx
0x080480f4 <+41>: inc    ebx
0x080480f5 <+42>: int    0x80

Key observation: The comparison is against 0x1337c0de. On little-endian x86, you must pass bytes \xde\xc0\x37\x13.


Solution

  1. Run the binary with the magic 4-byte token (little-endian):

    /maze/maze3 $(python -c 'print("\xde\xc0\x37\x13")')
    

    This satisfies the cmp [argv[1]], 0x1337c0de check in decoded d1.

  2. Enjoy the shell, then dump the next password:

    id
    cat /etc/maze_pass/maze3
    

    In the playthrough used for this write-up, the password printed was:

    deekaihiek
    

Why this works (tl;dr)

  • fine + l1 XOR-decode 44 bytes of d1 using key 0x12345678.
  • Decoded d1 checks argv[1] for 0x1337c0de and, on success, execve(“/bin//sh”).
  • Feeding \xde\xc0\x37\x13 (little-endian 0x1337c0de) flips the branch and spawns a shell.

Troubleshooting quick tips

  • If it exits immediately, confirm your argument is exactly 4 bytes and little-endian:

    python - <<'PY'
    import sys
    sys.stdout.write("\xde\xc0\x37\x13")
    PY
    
  • If ASLR trips you up while debugging, run under gdb with set disable-aslr on just to inspect the decode flow. (Not required to solve.)
  • Make sure your terminal doesn’t mangle nulls; we’re passing raw bytes, not a string.

Thanks for reading!

Until next time — Otsumachi!! 💖☄️✨

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