ENOWARS 3 WriteUp telescopy
16 July 2019 by alfink and Daniel Weber
Service Overview
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.
Conclusion
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.