Telescopy was a HTTP service written in Python which models an interface for storing information about planets.
For every planet you could store the name, declination, right ascension and a flag.
After registering you could see a list of all planets, add new planets or get planets.
On selecting a specific planet (sending a GET to /planet_details), you could retrieve its declination and right ascension.
As already hinted by its name you could not retrieve the content of the Flag field this way,
but of course there must also be a way for the gameserver to retrieve its flags.
When a new planet was added, the server answered the request with a generated planet–id.
Knowing this id and passing it along with a ticket to the Get Planet function, you could retrieve all information
(including the Flag field) about the planet where the id corresponds to.
As one could already guess, the gameserver stored flags in the Flag field of the planets and
used the previously mentioned Get Planet to check the availability of its flags.
Vulnerability 1: Generate Planet IDs
Now that our target is clear, lets take a look at how ids are generated and tickets validated.
Upon calling Add Planet the following function was called:
The interesting part happens inside of iding(p) as this is where our ids are generated and assigned.
As there is no secret value used for the hash calculation, we can just calculate the ids ourself.
Everything left now is to generate valid tickets.
Upon calling Get Planet the following code will run:
Reversing the ticketChecker binary was straight forward.
It takes a number as input, negates it and checks if the result is greater than 99.999.999 and prime.
If this holds the check succeeds, hence we have found a valid ticket.
Afterwards every ticket was stored in a Redis instance to make sure that no ticket is used twice.
Exploit for Vulnerability 1
Note that the code for the prime number generation is left out for better readability.
It can be found here.
Patch for Vulnerability 1
We just needed to make sure that the ids can not be calculated so we changed the iding function to append a secret value in the hash generation.
Vulnerability 2: Template Injection
As previously mentioned, a GET request to /planet_details with the parameter name would give us some information about the planet matching the name parameter.
The answer for this request was generated using Flask’s Jinja2 template engine.
The vulnerability in here was that our given name parameter was directly written to the template string using Python’s %s formatter, hence the engine executed everything we specified.
To test this we could use the following query:
which would result in the class of our specified object, hence <class 'list'>.
As we wanted to gain arbitrary code execution out of this, we used the base classes of our list object (list and object) to list every subclass of the object class:
On index 375 we found popen which allowed us to execute shell commands like this:
Exploit for Vulnerability 2
We used the remote code execution to steal the flags directly from the SQLite database file.
As we had no direct access to the stdout of our executed code, we created a file in /static containing the flags.
Patch for Vulnerability 2
To patch the vulnerability we replaced the %s formatter by a variable called namevar and gave the render function the input of the name parameter.
The first exploit was quite stealthy in the traffic because it looked like gameserver connections, but the second exploit was really noisy as one could easily notice the template injection inside the traffic.
As we guess due to the high amount of services, there were many teams which did not patch the vulnerabilities until the end of the competition.
The service was quite surprising as there was a PE–file called TicketChecker.exe which was completely unused. Nevertheless we had fun working on this service.