Robert Giczewski

Malware Analysis, Forensics, Threat Intelligence, Coding, Tech, Video Games

TrueBot Analysis Part IV - Config Extraction

13 Jul 2023 » malware_analysis, reverse_engineering, config_extraction


In the last post of the TrueBot series, I described some of TrueBot’s capabilities in more detail. In this post we will use this information to write a config extractor and extract the two RC4 keys, the mutex and the C2 IPs/domains.

Like in all config extractors, we need to somehow find the relevant things we want to extract.

Usually, I use YARA to navigate within the binary but this time, I wanted to try something different.
TL;DR - It’s not easier than YARA only, and it does not work in all cases.

To locate all the relevant stuff (RC4 calls, CreateMutex call, Base64 decode calls), we will use SMDA from Daniel Plohmann and then again YARA to navigate to the parameters we want to extract.

Find the CreateMutex call

To find the address of the call to CreateMutex, we use SMDA to get all API calls available in the binary and look for the string kernel32.dll!CreateMutex. In all observed TrueBot samples, there is only one call to CreateMutex, so we don’t have to deal with multiple calls.

    disassembler = Disassembler()
    report = disassembler.disassembleFile(args.file)
    functions = report.getFunctions()
    
    for fn in functions:
    	for addr, name in fn.apirefs.items():
        	if 'kernel32.dll!CreateMutex' in name:
            	mutex = tbot_extractor.get_mutex_string(addr)
                ...

When found the address, we use YARA to find the push instruction nearby the call to CreateMutex and just read it Malduck.

Find the Base64 decode calls

The next step will be to find the addresses of all the Base64 decode calls. The approach is identical to the one used for CreateMutex.

  • Get the address to the Base64 call via SMDA
  • Find the parameter pushed to that call via YARA

In most cases, the Base64 decode function is called two times, has eight outgoing calls and twelve basic blocks and that’s how we find it.

        # num_inrefs = How many time is the specific function called?
        # num_outrefs = Amount of outgoing calls inside this function
        # num_blocks = Amount of Basic blocks inside this function
        if fn.num_inrefs == 2 and fn.num_outrefs == 8 and fn.num_blocks == 12:
            for item in fn.inrefs:
                base64_decode_calls.append(item)

Find the RC4 decrypt calls

Finding the RC4 calls is a bit more complex, especially if you want to support many TrueBot samples. After looking into the RC4 calls of the tested samples, most of the time, the following patterns could be identified:

  • The RC4 decrypt function is called four times
  • Within the RC4 decryption function there are at most two outgoing calls
  • The number of instructions is greater than 50 or in some cases smaller than 32
  • The amount of basic blocks is 1, 4 or 8
  • Two of the RC4 calls are called within the malicious export

There might be some more and for newly observed TrueBot samples, a bit of fine-tuning might be necessary on these patterns. However, with those patterns, we will use again SMDA to find the addresses of the calls to mw_rc4_decrypt() and then move forward with YARA to find the arguments passed to the function.

Testing

Results and final code coming soon..

For the impatient, here is the code but beware. It is buggy and ugly.