Last month, Brooklynt Overflow competed in the UCSB iCTF competition. Unlike some of the other competitions we’ve been playing in lately, this was an attack/defense style competition in which we had a Virtual Machine containing 9 vulnerable services; our goals were to find the vulnerabilities within the services, write exploits to be launched against our competitors and patch our services before anyone could attack us.
The Challenge
In this write-up, we’ll be focusing on the vulnerable Water System Server. The code is broken into WaterSystemServer.py and MeasurementLib.py, which can be found below.
The first thing we notice is that the service starts out by asking the user for some measurements, which it then runs a basic calculation on. After analyzing MeasurementLib.py, we see that the calculation is based simply on the first digit of each of the measurements provided and determines if the frequency of each digit is within an acceptable range. With this knowledge, combined with a capture of an attack from another team, we were able to determine that the measurements had to match the following criteria:
`
{‘1’: 4, ‘2’: 2, ‘3’: 1, ‘4’: 1, ‘5’: 1, ‘6’: 1, ‘7’: 1, ‘8’: 1, ‘9’: 1}
`
In other words, we had to supply a measurement consisting of 13 numbers, with 4 of them starting with a 1, 2 starting with a 2, and the rest containing just one each of 3 through 9. Because the service running on each teams VM kept a log of what measurements if had already processed, we put together the following loop to generate new combinations each time we ran our code.
Now that we had gotten past the check, we were at the vulnerable section where we could retrieve the flag. The server asked for a command, which was then run through the following line:
This line of code performs no check on the data, and ultimately executes whatever is passed to it. From the inner-most function, it performs a Base64 decode, deserializes marshaled code, and finally executes the code with a parameter of clientsock. Reversing this functionality, we came up with the following python script to create our payload, and retrieve the flag:
In order to submit our exploit, we had to use iCTF’s Exploit template. While doing this, we realized that there was a backdoor to get the flag out that we had completely overlooked - if we entered measurements that did not pass the calculation check, but included the flag_id as the last element, the flag would be returned. The Exploit class provided always receives the flag_id as a parameter, but given that we had already constructed our payload, we continued with our original plan. Combining the previous pieces of code together in the Exploit template yielded the following code:
It took several tries to get our exploit accepted (we weren’t stripping the FLG from the returned flag originally, so the testbed wasn’t validating it properly), but ultimately the exploit was verified.
The Patch
Now that we knew how to attack the Water Server, it was time to correct our own service. We changed the code in two places: (1) To remove the backdoor with the flag_id and (2) To remove the vulnerable code execution that we took advantage of in our exploit. Correcting (1) was done by changing the line that sent the flag to instead close the connection, while (2) was fixed by looking for the function “callback” in the marshaled data and closing the connection if it was found. The modified code to protect our service can be found below.
Final Thoughts
This was a great competition, and a welcomed change from the jeopardy style games that have been running the past few weeks - a huge thanks to UCSB for all of their hard work setting this up! Brooklynt Overflow finished in 21st place, but we’ll be back next year to move up in the rankings!
Links
All of the code in this post can be found in the ISIS Lab Github CTF-Solutions Repo