OverTheWire Maze Level 4 → 5 tutorial!!
Published on 22 Sep 2023
Login
ssh maze4@maze.labs.overthewire.org -p <PORT>
# password: ishipaeroo
Binary: /maze/maze5
Task
This binary asks for a username and a key, both exactly 8 characters long.
If inputs satisfy the internal check in foo()
, we get a shell.
Code Analysis
Main logic:
int main(void) {
char user[9];
char pass[9];
printf(" Username: ");
scanf("%8s", user);
printf(" Key: ");
scanf("%8s", pass);
if (strlen(user)==8 && strlen(pass)==8) {
if (ptrace(PTRACE_TRACEME,0,0,0) == 0) {
if (foo(user, pass)) {
puts("Yeh, here's your shell");
system("/bin/sh");
} else {
puts("Nah, wrong.");
}
} else {
puts("nahnah..."); // anti-debug
}
} else {
puts("Wrong length you!");
}
}
So the check is hidden inside foo(user, pass)
.
The foo()
function
Reverse engineering gives us:
int foo(char* user, char* pass){
char p[9] = {0x70, 0x72, 0x69, 0x6e, 0x74, 0x6c, 0x6f, 0x6c};
// ASCII: "printlol"
for(int i = 0; i < 8; i++){
p[i] -= user[i] + 2*i - 0x41;
}
do {
i--;
if(i == 0) return 1; // success if all matched
} while(pass[i] == p[i]);
return 0; // fail
}
Key idea:
We must choose user[i]
such that p[i]
stays unchanged after subtraction.
That means:
user[i] + 2*i - 0x41 == 0
→ user[i] == 0x41 - 2*i
Constructing Inputs
Let’s calculate user
:
i=0 → 0x41 = 'A'
i=1 → 0x3F = '?'
i=2 → 0x3D = '='
i=3 → 0x3B = ';'
i=4 → 0x39 = '9'
i=5 → 0x37 = '7'
i=6 → 0x35 = '5'
i=7 → 0x33 = '3'
So user = "A?=;9753"
And pass = "printlol"
Solution
Run the binary:
/maze/maze5
Username: A?=;9753
Key: printlol
Result:
Yeh, here's your shell
$ id
$ cat /etc/maze_pass/maze5
Flag obtained:
epheghuoli
Why this works
- The key idea is keeping
p[i]
unchanged insidefoo()
. - Choosing
user[i] = 0x41 - 2*i
cancels out the subtraction. - So the comparison loop passes when we also supply
pass = "printlol"
.
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