It's the end of the year and a nice time to look at the security challenges posed by our Swedish SIGINT authority, FRA. If you want to take a shot at these challenges you can check out FRA Challenges. They usually release one or two per year. This year they released two, but for some reason removed the first one. Of course, they know that once something is on the Internet it's there forever, so here is a mirror of pcap-challenge-2019.zip.
Alright so let's take a look at the challenge. After taking a quick glance at the pcap file in Wireshark I quickly lost my hope, everything seemed to be TLS encrypted. I analyzed the handshakes and exported the certificates but everything checked out. Everything except the first TCP stream, which looked like some custom protocol.
Being a bit lost I tried to gather a better overview by analyzing the different conversations in the network log.
We can see that
192.168.218.101 asks the DNS server for
evildomain.hacker, followed by a TCP connection to
evildomain.hacker. Pretty suspicious indeed, maybe some kind of reverse shell.
192.168.218.103, on the other hand, is the one making all the TLS connections to the normal
Clearly, the focus should be on the conversation between
evildomain.hacker, which brings us back to the first TCP stream.
In the first TCP stream the following initial communication is made:
Nevertheless, it must be some kind of protocol and we must reverse it. It turns out that the procedure is similar to what I did in my other blog post where I had to reverse the communication protocol used by an Android app, check out Reversing the reverser.
Two important things to notice:
In the raw representation we can notice some patterns:
544c532f342e30 a2be748e 00000001 cbda 544c532f342e30 540431a7 00000001 216d559a67... 544c532f342e30 d5aa2d3f 00000002 a5dd49 544c532f342e30 a678eb01 00000002 890e8a7389...As we can see, the first part is always the same
544c532f342e30 which is just "TLS/4.0".
The second part seems random. The third part is sequence numbers to keep track of the requests.
Finally, the last part must be the data.
After checking for magic numbers and binwalking the pcap file it's quite clear that the data is not encoded using
a common encoding, rather I assume it's encrypted.
If it is encrypted, it must have an encryption key. I assume further that the attacker has had no previous possibility to exchange the key, implying that the key must be in the network log. For a given encryption method, the key length is usually constant. All this clearly points to the second part in the pattern above to be the encryption key. But what is the encryption method? Well, as pointed out, the ciphertexts have repetitions, e.g.
w:N.w:M.w:J.w:N.w:M.w:J.w:N.w:M.w:J.w:N.s:N.w:J.w:N.s:N.w:J.w:N.s:N.w:. AES-ECB can cause repetitions but the block size is bigger. My bet would be on XOR, it's simple to use and would cause similar repetitions with short keys.
Most straight forward implementation decrypt is to XOR byte
i from the cipher with byte
i%len(key) from the key.
Let's use the example above.
cipher = cb da key = a2 be ... plain = 69 64 i dNote how the first byte of the key (54) is repeated here.
cipher = 21 6d 55 9a 67 key = 54 04 31 a7 54 plain = 75 69 64 3d 33 u i d = 3I wrote a python script that would decrypt the network log and allow us to see what the attacker did.
As we saw, the attacker starts by running the
id command and gets the following answer
uid=33(www-data) gid=33(www-data) groups=33(www-data). The user id www-data
is mostly used by web servers. After sending a
pwd we learn that the code is executing from
/var/lib/wordpress/wp-content so that is where the malicious shell script is.
The attacker performs some more reconnaissance, looking for users and secrets (
find /home/ -iname "*secret*"). They find the top_secret_agent_report, but luckily they don't have access to read it, phew crisis averted. But wait, the attacker checks
sudo -l and learns that www-data can run python, as root, without a password! A nice one-liner owns the server.
Decoding the base64 results in a PNG image with the final flag.
sudo python -c "import os;os.system(\'/usr/bin/base64 /home/administrator/reports/top_secret_agent_report\')"
All in all I think this was a fun and interesting challenge.