Flare-On 11 - 05 sshd
This post will go into my write-up for challenge 5 of the Flare-On CTF that is hosted yearly by the FLARE team of Mandiant (Google). I did not manage to finish all of the challenges this year, but I learned some new things regardless and thought challenge 5 was a really well thought out challenge.

Premise
You receive a container image in a TAR file (my favorite), it's up to the participant to find the interesting files in this container to start our analysis for the challenge. I started out by performing some basic enumeration to see which folders we have and noticed a weird folder named /fmnt. Doing a quick grep on the system showed only 2 other files out of which there's only one file interesting enough;
I then checked for some strings within the coredump itself for some quick wins and seemed to get an initial hint to the xz backdoor from earlier this year;
It's pretty clear at this point we will have to analyze the coredump that is present in this container so I grabbed the /usr/sbin/sshd binary together with the coredump and started to analyze it.
Analyzing the coredump
To start analyzing the coredump I used gef which is a fancy wrapper for GDB to make your life a little more pleasant;
When we look at the stacktrace we get the following information:
Again we have a reference from liblzma.so.5 in our stackframes...
We can then inspect the stackframes listed with the frame <stacknumber> command, I started by looking at frame 0 and inspecting the registers. To make sense of this we also need to know what was running at what address in memory, we can use the i proc m command to do so. We can then annotate a little bit where each register is pointing to:
So the above doesn't give us a whole lot yet, we can also check the caller of this routine by looking at stack frame 1, suspiciously this is the frame of liblzma.so.5 which was recently in the news because of the xz backdoor. The RIP register is pointing into the memory space of liblzma.so.5 before the crash so it makes sense to zoom in a little bit more into this;
So this shows us that dlsym was called with a certain value and for whatever reason our program crashed after calling this. Let's see what function of the liblzma.so.5 we're looking at by looking at stack frame 2 and printing the disassembly around the RIP again:
Checking where the call for RSA_public_decrypt is taking us:
liblzma.so.5
With the above information I loaded the liblzma.so.5 binary into BinaryNinja to find the above routines in the binary and see what was going on:
Interestingly enough the dlsym loaded the RSA_public_decrypt value and passed this function as its return. A thing to note is that this function contains an extra space at the end and could indicate that it is trying to load a function that doesn't exist RSA_public_decrypt (without the space at the end), therefore crashing the dlsym call.
In the above disassembly snippet we can already observe a weird comparison being done, it becomes a bit more clear in the decompiled output;
This check is done on the value present in RBP, a quick check reveals this is indeed the case:
After some annotation of the shellcode we have a function that looks like the following:
The above code loads a blob of data which is shellcode that is encrypted with ChaCha20, decrypts this shellcode and then executes it. The key and nonce used for the decryption can be found in the RSI register;
A very simply Python script can then be used to decrypt the blob of data that was found in the liblzma.so.5 binary;
Shellcode
This new functionality leads to some network connections being set up towards 10.0.2.15:1337 and receive 3 packets of different lengths; packet 1: 0x20, packet 2: 0xC, packet 3: 0x4, totaling 48 bytes;
During the string "analysis" I noticed a string in the coredump that led me to an easy way to uncover the above information we need to decrypt whatever buffer was read from that file;
If we look at the pattern of the packets received we can lay out the order:
Another clever way of finding this information came from a friend of mine who calculated back using the stack offset of the function called for initializing the ChaCha20 routine;
As the function is called the stack sits at 0x16b0, going from there I simply started dumping hex values until I saw a pattern emerging and a piece of hex that could indicate a size. This let me to: $rsp-0x16b0+1024 -> points to start of chacha20_packet, there's a RSP-0x1688 at the start of the function connecting to the C2 and receiving the packets. We need to calculate backwards to get the contents of the ChaCha20 key packet, nonce packet and pathname packet.
Decrypting the buffer
The last part had me stuck for quite a while as it turned out that this was a modified ChaCha20 routine where the constants were changed... We can now go 2 ways:
Change the Python package we used earlier to use different constants;
Emulate the shellcode's routine using something like unicorn;
I opted to go for the emulation as I had too little experience with this and it turned out to be a good exercise.
Because the stack is not that large we can simply dump it and traverse it byte-by-byte until we get output that is somewhat resembling the flag format that we expect. And we need to patch the 64th byte of each buffer we're attempting to contain the @ character so it matches the struct that is being build in the shellcode.
You can find the emulator code in this gist :)
Conclusion
Thank you FLARE team for another year of cool challenges, challenge 7 really underlines the need for me to dive more into the basics of cryptography and why it works ;) This challenge learned me some new cool tricks that will sure prove to be useful in my day-to-day job as well!
Last updated