]> git.somenet.org - pub/jan/ctf-seminar.git/blob - writeups/aliponti/ructfe19.md
Add writeups Alexander Ponticello
[pub/jan/ctf-seminar.git] / writeups / aliponti / ructfe19.md
1 # RuCTFe 2019
2
3 I played RuCTFe 2019 on-side in Saarbrücken with the local [saarsec.rocks] team. It was a very cool experience playing with the reigning champion and an overall experienced attack-defense team, getting to know their setup and tactics. The CTF was also well made, most of the challenges had an adequate difficulty providing a tense competition until the last second. Please note that this is writeup is basically a textual version of the talk I gave about this service on 17/12/2019. I spent the more or less the whole 9 hours of the CTF working on this challenge. In addition, I attended a preparation meeting by saarsec, where they gave an introduction to attack-defense CTFs in general, talked about tactics and explained their infrastructure. They also had a small example challenge prepared for participants to exploit using the tools they provided.
4
5 ## Locator
6
7 I work on the locator service together with Markus Bauer. The service was, similar to the whole CTF, submarine themed. One could register its submarine by providing a name, password and some additional metadata such as speed and color. It was further possible to add custom fields during registration. This was used by the gameserver (GS) to store the flag in a field named "CARD". After registering successfully, one was presented with a radar-like interface, showing some blinking dots. It was also possible to display the metadata saved in the previous step under the `/info` endpoint.
8
9 ### The vulnerability
10
11 This already made it clear what we want to achieve, namely access other users (the GS) info data. In order to find a vulnerability we looked through the code and fund a hardcoded `secretHashKey`, which was used in the context of session generation (see Figure 1). 
12
13 [Fixed hash key](fixed_secret.png)
14
15 So our first thought was that maybe we are able to generate a valid session for another user. We generated some sessions for ourself, trying to find some pattern in it but without any luck. So we looked further into the code in order to understand, how sessions generation was done and where the hardcoded secret was used. But by doing so, we stumbled over something even more interesting. The service included a class with the sounding name `RuntimeClassLoader`, which did exactly as its name suggests, loading classes at runtime. 
16
17 [RuntimeClassLoader class](class_loader.png)
18
19 This is known to cause problems [1](https://stackoverflow.com/questions/42720149/what-is-a-purpose-of-the-secureclassloader), also the IDE I used highlighted already that the `defineClass` method used had been deprecated. If we are able to inject arbitrary bytecode into this method, we have a potential RCE. We found one usage of the method in question in the `Decoder` class `isValidKey` method, which gets its input from the `isValid` method of the `RegisterData` class. This class is annotated as serializable. We figured out that the registers frontend sends the data as a json-encoded serialized Java object. So in order in get our custom class loaded we have to compile it, encode it and send it to the `/register` endpoint as a custom filed. Another important puzzle piece was found in the `decodeMessage` method of the `Decoder` class. Here, the previously input class gets instanciated, resulting the execution of its constructor method. We can use this to execute arbitrary code and retrieve the output via a call to the `/info` endpoint. 
20
21 ### The exploit
22
23 In order to exploit this, the first thing to do is write a Java class where to constructor contains the code one wants to execute. In this case, we retrieved a list of users and read their message data containing the flags. We made use of the service's OR mapping functionality. The so build Java class can the be compiled, also using the pre-build Java jar. By doing so we obtain the Java bytecode. This can then be send to the `/register` endpoint, along with the other necessary data. We did so using a python script and the requests library. The output of the code can then be accessed via the `/info` endpoint.
24
25 [Java class used in the exploit](exploit_class.png)
26
27 #### Considerations
28
29 Since this was an attack-defense CTF, there are some additional steps one might take in order to obfuscate their exploit before using it against other teams. This is especially useful if no other team has exploited the service yet. If one uses just a vanilla exploit, other teams will be very quick in copying it and exploiting themself, diminishing the points one gets per flag. Also, blocking your exploit becomes easier, the more distinguishable it is from ordinary traffic. So the first measure we took was sending our requests with the same User-Agent the GS used. Next, we did not export the flags directly, but encoded them first using the base64 encoding, as well as some string replacement operations. Of course, one could go even further and extract flags only piece-wise or through a side channel such as a TCP socket etc.
30
31 ### The fix
32
33 Before exploiting the services of other teams, we tried to fix our own service, since we expected other teams to copy our exploit rather quickly. So we deployed a quick-and-dirty fix by filtering out every request that was larger than a certain threshold. We check typical GS requests to the `/register` endpoint and noticed that they where significantly smaller than requests produced by our exploit. This fix proved sufficient for the rest of the CTF since it constrained other teams well enough in the exploiting that we did not loose any flags. Of course, in a real-world scenario this is not an ideal solution. A better patch would be letting `RuntimeClassLoader` inherit from `SecureClassLoader` provided by Java/Kotlin and restrain the loaded class accordingly. This could prove efficient since a standard class used by this service does not run any code so restrictions could be quite strict.
34
35 [Patched RuntimeClassLoader class](patch_classloader.png)
36
37 ### Further developing the exploit
38
39 When the fix was deployed and the first version of the exploit was busy stealing flags, we had still plenty of time left in the CTF to become creative with this RCE. The most promising idea that came up was, using the RCE to patch other teams services in order for them to not be vulnerable to the exploit any more. This should prevent other teams from exploiting weaker teams who did not patch their service and where therefore an easy target. Of course, in order for us to be able to still exploit the service, we implemented a backdoor in our patch. So we patched the `RuntimeClassLoader` class to include a blacklist for the `loadClass` method, filtering out malicious functionality such as accessing user messages. The backdoor was a simple hard coded string that let every input get through. We then modified our exploit Java class to replace the `RuntimeClassLoader` in the original service jar with our adapted version. 
40
41 [Placing the modified class in the jar](patch_class.png)
42
43 Lastly, we had to force the container to restart to use the patched version of the code. This was easy, since the Docker-compose configuration used had `restart: always` set, meaning the container restarts automatically once the executed program terminates. So we could just call `System.exit(0)` inside our malicious Java class. During the CTF, other teams adapted their exploits to bypass our implemented patch, which in order made us adapt our blacklist and repatch services through our backdoor and so on and so fourth. 
44
45 [Final version of the patch blacklist](patch_advances.png)
46
47 ### The take away
48
49 Obviously this is a very constructed service, designed specifically for this attack-defense format. As such it does not represent a very real-world scenario. However it does a good job at highlighting the possibilities of an RCE vulnerability. Also, one can see that deprecated functions are so for a reason, i.e. should not be used especially if security critical. Also, in my opinion, loading Java class during runtime is a questionable practise and should only be done with extreme caution, there are probably other software engineering pattern one should use in such a situation.
50
51 ## Time report
52
53 I spent the whole day onside during this CTF, so around 11 hours on that day. In addition I took part in preparation meetings of saarsec leading up to the CTF where I got to know the infrastructure and was able to participate in a short practise CTF in order to get a hand-on experience with the tools, around 4 hours. Reporting clocked in at another 4 hours all together.