FRA Challenge 2019-1

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.

First glance

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.

Taking a step back

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 server.secret.net.fra and 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 fra.se server. Clearly, the focus should be on the conversation between 192.168.218.101 and evildomain.hacker, which brings us back to the first TCP stream.

tcp.stream eq 0

In the first TCP stream the following initial communication is made:

TLS/4.0..t.......
TLS/4.0T.1.....!mU.g7..#s..5pP.tcX.i7..#sF.0eE.}$V.;qA.i7..#sF.0eE.}.
TLS/4.0..-?......I
TLS/4.0.x.........s...c...s...d...v.U.n...o.r
I have to agree with Dolores on this one, this doesn't look like anything to me.
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:
  • The content is not human-readable, so either encoded or encrypted.
  • Observing later packets we see a lot of repetitions in the ciphertexts, which implies poor encryption.

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.

Decrypting

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  d 
Note 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  =  3
I wrote a python script that would decrypt the network log and allow us to see what the attacker did.

Interpreting the attack

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 (cat /etc/passwd, 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.

sudo python -c "import os;os.system(\'/usr/bin/base64 /home/administrator/reports/top_secret_agent_report\')"
Decoding the base64 results in a PNG image with the final flag.

All in all I think this was a fun and interesting challenge.


Write your comment!

Comments

Goldilocks !52244cd3cd5b No. 90 >>94 2019-12-29 16:35:19
Very cool! :D
Benjamin !d2782292df32 No. 94 2020-01-09 20:38:05
>>90
Haha thanks! :D
I'm glad it wasn't real TLS.