TTPs: BadStrings
In this writeup we discuss a mutli-step methodology for beating string detection by Mandiant's FLOSS string deobfuscator
Last updated
In this writeup we discuss a mutli-step methodology for beating string detection by Mandiant's FLOSS string deobfuscator
Last updated
Strings are reasonably common in executables, and any reverse engineering class will cover the utility of dumping an executable's strings to discover critical features of the executable's functionality. To mitigate the discovery of capabilities by analysts, malware authors will often encode, encrypt, or hash strings to obfuscate critical functionality. Most commonly, malware will implement XOR encryption to hide strings from basic analysis.
However, modern tools allow for emulating the decryption function and consequently discovering those key strings. One of these tools is Mandiant's FLOSS.
This tool is a powerful utility for analysts. FLOSS can deobfuscate the key strings encrypted by malware authors and help analysts unmask key executable functionality using advanced static analysis. In this writeup, we'll be closely examining the effect this can have and one way to bypass this analysis.
Below we have a pretty basic program that prints out our "secret".
We can quickly compile this program, and run the "strings" utility on it and discover that our secret is indeed exposed.
Now, we can use an online utility, python script, or additional C program to encrypt our secret string with the XOR algorithm. We'll be using CyberChef in this writeup, but any approach to obtaining an XOR'd string would work.
If we write the appropriate code to decode our secret, we'll end up with something like the picture below.
On the left, you can see the source code of our program. We take the encrypted secret, hardcode the key and length into our program, initialize our decrypted buffer, and then call our xor_func(). This function will then take our encrypted string, decrypt it with our key, and store it in our decrypted buffer. Once that decryption is complete, our program will print our secret.
On the right, you see that our program functions as expected, and we can no longer find the secret using the "strings" utility.
But what happens if we run FLOSS on our program?
Here we see that FLOSS can decode our string, which is unfortunate for our malware development. However, we can notice something interesting by combing through FLOSS' recent updates.
It looks like we can trick FLOSS into overlooking our secret but referencing the string with another function. Lets check out how we might do that.
If we go back to CyberChef, lets add one easy manipulation. Lets add one to our output values in our array like so.
Now that we've done that, we can quickly implement a decrement function in our C program that will allow us to have a second reference to our obfuscated string and trick FLOSS into overlooking our shenanigans.
Here we can see that this simple trick has in fact allowed us to retain our program's functionality, the strings function still can't find our secret, and our secret string is unobtainable with FLOSS.
It would be pretty cumbersome to write and maintain two functions every time we wanted to unobfuscate a string in our C code. Thankfully, C provides us macros that we can use to automate this process a little bit and maintain clarity in our code.
In the above picture we see that at the top of main.c we declare the DECRYPT_STRING macro. This macro is processed by the compiler, so that the substitution we defined occurs before compilation. In this way we can more clearly see and maintain our code without having to find every pair of function calls and modifying them individually. In this proof of concept the change is largely cosmetic, but in larger projects like Revenant it's important to leverage this capability for clarity.
In this writeup, we saw that it's possible to bypass FLOSS' analysis by executing multiple manipulations on our obfuscated strings, and we saw that it's possible to consolidate these manipulations into a single C macro for clarity in our code. Revenant leverages a similar implementation of this functionality to similarly bypass FLOSS analysis and maintain the obfuscation of crucial information like IP addresses, UserAgent, and dlls that would facilitate the development of signatures for our implant.