FAUST 2022 - ghost writeup
FAUST 2022 - ghost writeup
This challenge was a “hidden” service for FAUST 2022 CTF.
The missing service
During this CTF, our deploy for the challenges was a bit weird and when we decrypted them we only found 7 services. Ghost was completely missing, and when the CTF started, unexpectedly, it was reported down.
It took us three hours to find the service and succesfully boot it, a gentle reminder that using the provided vulnbox is always a better idea :)
Python, inside python, inside bash, inside bash, inside a binary
As the service was a “backdoor” it was lightly hidden inside a “setup” binary.
After fiddling a bit with gdb, unpacking many bash scripts, and fixing broken python indentations, we managed to extract the source code of the backdoor:
1 | import json |
First thing we did was to analyze the s array, we found that it was just an array of chars shifted by 7, and printed them all.
1 | 0: [ |
Analyzing the rest of the code we realized that the backdoor was requesting commands from a server and returning results to a different port on it.
Bringing the service up
Our first priority was bringing the service up, as at this point we were still not earning points for it.
Our quick patch was to edit the final for of the program in this way:
1 | from time import sleep |
And running the code. This magically worked and we got our sweet sweet backdoor installed.
(Guessing) The exploit
Reading the source code we could find some comments, telling a bit of lore behind the backdoor’s code.
In particular:
1 | # We only have one IP here. |
As usually comments are not left in a backdoor, we expected them to be needed for the exploit.
After a lot of brainstorming with my teammates and many random guesses, we realized that the comment was hinting us that the main server was connecting not only to the checksystem requesting for commands, but also somewhere else.
As the only way for this to be expoitable was for the server to ask us for commands, we tried opening a listener on the vulnbox on the port 1236 (as the comment says that the protocol is the same), and after a few seconds we got a connection!
We then sent the command {'cmd': 'GETFILE', 'outid': 'AAAAAAAAAA', 'sender': 'AAAAAAAAAA'}
and listened on the other port (3334) for responses. Immediatly after we got a connection, which contained flags base64-encoded!
Now the task was automatizing it.
We set up two services on our vulnbox: one was sending commands, the other was collecting responses and submitting flags.
The sender code:
1 | import socketserver |
The receiver code:
1 | from pwn import * |
Patching the service
At first look the service seems unpatchable, however a detail in the communication protocol allows for returning flags only to the checksystem.
Every request storing a flag had a ‘outid’ key, specifying the name of the file where to save the flag.
Afterwards, the checksystem (and attackers) would retrieve the flag using the GETFILE command, which however ignores the ‘outid’ parameter and simply dumps all files in the directory.
By filtering and returning the content of only the requested file an attacker could not guess the correct ‘outid’, while the checksystem, knowning it, will be able to retrieve the flag.
However, while patching, we must have missed something, as our service went in “recovering” state for the rest of the CTF, making us loose many points (and the third place :( ).
As of now we are still unsure of why the service was not fully working according to the checksystem.
This is the patched code:
1 | import json |