Upload an empty file to circumvent file size restriction (enforced only on the first phase). Right before execution is resumed, overwrite the file with ret2win payload by abusing Race Condition.
Basic File Checks
Nahmnahmnahm is a simple file reader that takes in a file path as input and print out it's content.
┌──(kali💀JesusCries)-[~/Desktop/CTF/NahamCon CTF 2023/nahmnahmnahm]
└─$ ./nahmnahmnahm
Enter file: ./test
Press enter to continue:
test
To prevent quick wins, the binary will verify if the user-input contains the keyword flag before proceeding with printing the file content.
Checking the binary protection shows nothing significant other than NX is enabled. Could this possibly be hinting that we need to write our payload into a file and execute it using the binary's read functionality to bypass NX?
┌──(kali💀JesusCries)-[~/Desktop/CTF/NahamCon CTF 2023/nahmnahmnahm]
└─$ checksec --file=nahmnahmnahm
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 86 Symbols No 0 3 nahmnahmnahm
Static Code Analysis
There are several other functions besides main(), so we'll be going through them one at a time.
In the main() function, the program will ask for a filename. The filename can’t contain the keyword flag in it. Then, the program will perform some checks to verify if the file is a symbolic link file, and if the size of the file is greater than or lesser than 80 bytes. If all the above conditions are met, it will pass the filename to vuln() function as its argument.
In the vuln() function, the program reads the content of the file in the variable filename using the fopen(), with a maximum data reading size of 4096 bytes (0x1000). Then, stores it in the variable buf, which can only hold data up to 80 bytes. This causes a buffer overflow vulnerability that can be exploited to call the winning_function() to achieve ret2win.
In essence, the programs only perform a one-time check in the main() function before pausing the program execution with a getc() function that waits for the user to enter any character. This introduces another exploitable loophole known as Race Condition.
The plan is to create an empty file and input the name of the file as our input and wait for the program to enter an idle state, before writing our ROP/ret2win payload into the file. When the program execution resumes, it will call the vuln() function, and leads us to winning_function() because of the buffer overflow vulnerabilities.
Think of this like Command Line Spoofing, where a process is first created in a suspended state with benign command line arguments. Right after the verification/logging procedure is over, the arguments can then be overwritten with any malicious inputs.