On 2024-10-22
by Adams Kone, Incident Responder on assignment at Airbus Protect

Incident Response: Analysis of recent version of BRC4

CSIRT blogpost
Summary

Introduction

During our latest incident response case we have discovered a recent sample of Brute Ratel C4 packed with Themida. BRC4 is a powerful Command and Control (C2) tool which allows to control targeted workstations through an executable agent. The objective of Themida is to protect code against reverse engineering.

Currently, C2 tools are used by attackers as much as pentesters. So, it’s always interesting to analyse and to fully understand them in order to find a way to detect them effectively and enrich the threat hunting phase.

This sample is a DLL from an archive that has been brought to the targeted machine. It was executed with the command line present into the following event:

Analysis of recent version of BRC4

The difficulties behind this sample were:
• Unpack Themida
• Defeat obfuscations and anti-debug techniques
• Understand the different stages to reach configuration and data sent

Here you’ll find a short explanation of the different stages:

Analysis of recent version of BRC4

This article solely focuses on obfuscation techniques, configuration extraction and how data are encrypted before they are sent to the server.

To briefly summarize, to pass the first stage, we used ScyllaHide plug-in on xDBG and we jumped in a specific area with read/execution rights at the time the DLL was loaded. In this area, we found symbols that allowed us to find the loader of BRC4.

The second stage is just a loader of shellcode where ZwAllocateVirtualMemory, ZwProtectVirtualMemory, ZwCreateThreadEx and NtWaitForSingleObject functions are used for self-injection. We save the shellcode with system informer to get a new starting point for the analysis.

The third stage focuses on the first part of the shellcode with obfuscations and anti-debug techniques. It introduces the last stage by self-injection with NtQueueApcThread.

The last stage is the most interesting part of the shellcode because it focuses on configuration and communication.

In the following article, I will use a first part to describe what obfuscation techniques were used in stages 2, 3 and 4 and unpacking process of Themida. I will then describe how stage 4 retrieves the configuration, uses it to cypher outgoing data, and how we can automate retrieval of this configuration.

Unpack of Themida

For the unpacking part we used ScyllaHide plug-in on x64DBG with Themida x86/x64 profile.
We found two different results for two types of execution: normal execution on the left and
execution with ScyllaHide on the right:

Name Win.dll
SHA-256 4400750cbc597b7e0cec813dcaf66d00e83955a034591a5a6ba40547a045721b
File type PE64
Packer Themida 3.x

For the unpacking part we used ScyllaHide plug-in on x64DBG with Themida x86/x64 profile.

We found two different results for two types of execution: normal execution on the left and execution with ScyllaHide on the right:

Analysis of recent version of BRC4

After the execution with ScyllaHide plugin, we found a memory area with execution right and we jumped on it:

Analysis of recent version of BRC4

During the analysis of this memory area, we finally found a main function, this function is our second stage:

Analysis of recent version of BRC4

Obfuscation & Anti-debug

During our analysis we found obfuscations based on scraping, PEB parsing and API hashing on stage 2, 3 and 4.

Here, a part of code of stage 3:

Analysis of recent version of BRC4

We can find multiple functions, their role is:

  • Introducing anti-debug techniques
  • Load modules with PEB Parsing
  • Load pointers of functions with PEB parsing and API hashing
  • Build syscall routine
  • Resolve syscall ID with scraping

Anti-debug

An anti-debug technique involving PEB parsing is used to compare the value at PEB+0xbc with 0x70. This code is encountered two times in the stage 3, it’s not evident to spot it, so we must analyse the code step by step.

Analysis of recent version of BRC4

Load modules with PEB Parsing

The pointer to the base address of each module is found with PEB parsing. In this case the program makes a loop in the _PEB_LDR_DATA structure to scrape these bytes: 0x5A4D. This technique is used to avoid calling direct functions such as LoadLibraryA which allows to load DLL.

Load pointers of functions with PEB parsing and API hashing

To resolve the pointers of the functions, the program parses the PEB structure and then makes a loop in the name pointer table in IMAGE_EXPORT_DIRECTORY structure. A call on the hashing function is operated for each name in the table to find the correct function for the requested hash. Once the correct function is found, its pointer is obtained using the address table. You’ll find an article describing the process in a more detailed way here.

Build syscall routine

To be stealthier than its previous version, the program builds its own function to make a syscall. In an older version, there were obfuscation techniques for the loading function and ID resolution, but at the end there was a direct syscall advising us for an incoming self-injection.

In the capture below, the program builds a pointer to a custom code section to execute a syscall.

Analysis of recent version of BRC4

Resolve syscall ID with scraping

The code doesn’t use a direct syscall ID to be able to target enough workstations regardless of their version. The ID of a syscall depends on the version and build number of the Operating System: with this technique, it’s not necessary to obtain the OS version of the targeted workstations. All ID are presented on j00ru website. On the screen below, we can see the specific section which resolved a syscall ID. The code scrapes a specific sequence of bytes to find out the correct position of the ID. This process depends on the function previously loaded with PEB parsing & API hashing:

Analysis of recent version of BRC4

For a better understanding, on the screen above we have the function used to find syscall ID thanks to bytes sequence on the left. On the right, we have the code of NtAllocateVirtualMemory which allows us to understand why these following bytes are targeted: 0x4C, 0x8B, 0xD1 and 0xB8. The bytes 0x4C, 0x8B and 0xD1 are respectively:

MOV R10, RCX

The byte just after 0xB8 is the syscall ID, in normal execution this value is moved into EAX like that:

MOV EAX, [SYSCALL ID]

To resume this part, the malware uses these techniques to be stealthier that allows it to evade detection against security software.

These techniques allow to the malware to:

  • Hide functions into Import Address Table (IAT) to evade detection
  • Counter dynamic analysis by stopping the program
  • Counter userland detection by IAT hooking by using direct syscall

Last stage

The last stage focuses on configuration extraction and encryption of data before they are sent to the C2 server. In addition, we can find obfuscation functions which loads DLL and function with symbols to communicate with C2 server. In our case, the BRC4 shellcode uses ws2_32.dll library, the HttpSendRequest function to send data and the InternetReadFile function to read
data.

During this stage, we can retrieve all BRC4 principal functions using the NOP value (0x90), because each function is separated from the other by NOP instructions. The number of NOP depends on the size of the functions, since the purpose of the NOP instructions is to correctly align the stack.

Before continuing, here a diagram explaining the process between the configuration and the encryption of data:

Analysis of recent version of BRC4

Configuration extraction

Like its previous version, the configuration is encrypted in RC4. The key and the encrypted configuration are found at this precise moment during the last stage:

Analysis of recent version of BRC4

Shortly afterward, we can retrieve the configuration in clear text. Because it takes a lot of time to reverse all the code until this moment, we prepared a configuration extractor based on specific patterns in the program’s memory. The conditions to fulfil to be able to use our extractor program are:

  • Get the BRC4 shellcode in the loader stage (real first stage, because Themida packer is an added stage by our adversary in this instance).
  • Get this shellcode loader here for our configuration extraction process.
  • Get the configuration extractor on Airbus Protect GitHub.

The configuration extractor is a python program which uses the Ctypes library to execute our shellcode through a loader. The handle of the new process is used to obtain memory areas with VirtualQueryEx. Here, we focus on memory areas with these specific conditions:

if mbi.State == MEM_COMMIT and (
 mbi.Protect == PAGE_READWRITE or
 mbi.Protect == PAGE_READONLY ):

Each matched memory area is saved in the same dump file. At the end of this function, the dump is used to extract the key and the configuration by using these regexes:

regex_sequences_forKey = [
 r"(00){16}([1-9A-Fa-f]{1}[0-9A-Fa-f]{1}){8}([0-9A-Faf]{2}){8}(00){8}(..0001......)(00..)",
 r"(00){16}([1-9A-Fa-f]{1}[0-9A-Fa-f]{1}){8}([0-9A-Fa-f]{2}){8}(00){8}([0-9A-Faf]{2}){6}(0001)",
 r"(00){16}([1-9A-Fa-f]{1}[0-9A-Fa-f]{1}){8}([0-9A-Fa-f]{2}){8}(00){8}([0-9A-Faf]{2}){6}(0010)"
 ]
regex_sequences_forConfig = r"(4883e4f04831c0505468)"

We performed some tests on samples discovered on Virus Total with Yara rules created for this specific version of BRC4. These Yara rules are available in the Yara section of this article. Here, the results of our extractor on five samples (each test is operated on the shellcode extracted from a DLL file):

Case sample – 4400750cbc597b7e0cec813dcaf66d00e83955a034591a5a6ba40547a045721b

Analysis of recent version of BRC4

sample 1 – 780b2b715aa33e8910479a671469ad27cc88a7ed513b83e43cf7a6a16f613013

Analysis of recent version of BRC4

sample 2 – 04b47b5492f5b2086e4a6b3f2bef73eb15a51140a86bcd05417d00bf6875ffb6

Analysis of recent version of BRC4

sample 3 – 9ec67f1914603e729a3b6bafe3a96cdc660717ca7dfb457290f68fc56dd0a5e2

Analysis of recent version of BRC4

sample 4 – bd32cbb6c08eff7fc6aa0bfe2fd81ec467f70d9b726015859da39744271bbcb0

Analysis of recent version of BRC4

Encryption of data

Before sending data to the C2 server, the program uses RC4 to encrypt them. The first request is just a simple JSON containing data about the victim, like a profile. The first request before encryption looks like this:

{
 "cds": { 
"auth":"[C2_PASSWORD]"
 },
 "mtdt": {
 "h_name":"[VICTIM_MACHINE_NAME]",
 "wver":"x64/100",
 "ip":"[VICTIM_MACHINE_IP], 0.0.0.0, 0.0.0.0",
 "arch":"x64",
 "bld":"17763",
 "p_name":"[PATH_OF_EXECUTABLE_IN_BASE64]",
 "uid":"[VICTIM_USERNAME]",
 "pid":"6992",
 "tid":"5448"
 }
}

Explanation of data fields:

  • C2_PASSWORD: password for C2 authentication
  • VICTIME_MACHINE_NAME: the name of the victim machine
  • VICTIME_MACHINE_IP: the IP address of the victim machine
  • PATH_OF_EXECUTABLE_IN_BASE64: the path to the base64-encoded executable
  • VICTIM_USERNAME: the session username of the victim machine

RC4 encryption is operated by SystemFunction033 from cryptsp.dll:

Analysis of recent version of BRC4

This function is an alias of SystemFunction032 because both point to the same relative address:

Based on ReactOS documentation, this function operates an RC4 encryption routine:

NTSTATUS
WINAPI SystemFunction032(struct ustring *data, const struct ustring *key)
{
 RC4_CONTEXT a4i;
 rc4_init(&a4i, key->Buffer, key->Length);
 rc4_crypt(&a4i, data->Buffer, data->Length);
 return STATUS_SUCCESS;
}

To remind you, here the decrypted configuration for our case:

[+] Config txt -> 
||2|1|0|100|||||||eyJjb29raWUiOiI=|In0=|eyJibG9iIjoi|In0=|eyJIVFRQIjoiU1VDQ0VTUyJ9|1|1|
206.166.251.128|8081|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 
(KHTML, like Gecko) Chrome/90.0.4430.93 
Safari/537.36|password|MJSBLHLU6B8VG7JP|/test.asp|Y29udGVudC10eXBlOiBhcHBsaWNhdGlvbi9vY
3RldA==,cmVmZXJyZXI6IGdvb2dsZS5jb20=|d0cf9d2be1473579e729382f5c2e22c6453a93478a733b2f28
f86078cec0889b

In this table you will find the elements that interest us and that will allow us to understand in a little more detail how the data will be processed:

C2 IP 206.166.251[.]128
C2 PORT 8081
HEADER PARAMETER 1 (user-agent) Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/90.0.4430.93 Safari/537.36
HEADER PARAMETER 2 (content-type) content-type: application/octet
(Y29udGVudC10eXBlOiBhcHBsaWNhdGlvbi9vY3RldA==)
HEADER PARAMETER 3 (referrer) referrer: google.com
(cmVmZXJyZXI6IGdvb2dsZS5jb20=)
PASSWORD FOR AUTHENTICATION password
PASSWORD TO ENCRYPT DATA TO SEND MJSBLHLU6B8VG7JP
PAGE TO COMMUNICATE /test.asp

In this context, we know that the first request will be send to http[:]//206.166.251[.]128:8081/test.asp with the header parameters provided in the table above. Before sending to the C2 server, the program will encrypt the data with the password
(MJSBLHLU6B8VG7JP) in the table and it will make this request with encrypted data:

Analysis of recent version of BRC4

Conclusion

Our in-depth analysis of Brute Ratel allows us to highlight the complexity behind all techniques seen in this article.

We explored various obfuscation techniques that complicate analysis for experts and detection by security software. We also covered the importance of configuration, a key element of the program. This not only enables the retrieval of agent configuration data, including the IP address and port of the command and control (C2) server, but also the passwords used for authentication and encryption of data to be communicated to the server.

Finally, we’ve set up a configuration extractor that allows us to quickly retrieve the agent’s key elements.

Year after year, malware grows in complexity, and we must continue our research to help the community to detect effectively. We hope that the findings and tools presented in our research will help you.

 

Detection

Yara

import "pe"
rule stage_Loader {
 meta:
 author = "Adams KONE"
 company = "Airbus PROTECT"
 sharing = "TLP:CLEAR"
 category = "MALWARE"
 description = "Loader’s stage"
 
 strings:
 //Obfuscation technique
 //API hashing function
 $HashingFunction = { 
 31 D2 0F BE 01 84 C0 74 14 01 D0 48 FF C1 69 C0
 01 04 00 00 89 C2 C1 EA 06 31 C2 EB E5 8D 04 D2
 89 C2 C1 EA 0B 31 D0 69 C0 01 80 00 00 C3
 }
 //Obfuscation technique
 //Function to resolve syscall ID
 $getIdForSyscall = {
 80 79 FF CC 74 58 45 85 C0 75 04 48 83 E9 20 44
 8A 09 41 80 F9 E9 74 0A 44 8A 41 03 41 80 F8 E9
 75 07 FF C2 45 31 C0 EB D7 31 C0 41 80 F9 4C 75
 2F 80 79 01 8B 75 29 80 79 02 D1 75 21 41 80 F8
 B8 75 1B 80 79 06 00 75 17 0F B6 41 05 C1 E0 08
 41 89 C0 0F B6 41 04 44 09 C0 01 D0 EB 02 31 C0
 C3
 }
 //Obfuscation technique
 //Function to forge a pointer to syscall, ret instructions.
 $getAddrToJumpToSyscallAndRetInstruction = {
 48 89 C8 48 8D 51 14 80 38 0F 75 0C 80 78 01 05
 75 06 80 78 02 C3 74 0A 48 FF C0 48 39 C2 75 E7
 31 C0 C3
 }
 //Obfuscation technique
 //Hash of functions
 $ZwProtectVirtualMemory = { E0 0E BB 82 }
 $ZwAllocateVirtualMemory = { BF 06 3A E3 }
 $NtWaitForSingleObject = { 26 6E C2 E2 }
 $NtCreateThreadEx = { AA 5D F1 E5 }
 condition:
 (uint16(0) == 0x5a4d and uint16(uint16(0x3c)) == 0x4550) and all of them
}

rule Stage_BRC4_Part1 {
 meta:
 author = "Adams KONE"
 company = "Airbus PROTECT"
 sharing = "TLP:CLEAR"
 category = "MALWARE"
 description = "First part of the shellcode’s stage"
 
 strings:
 //Obfuscation technique
 //API hashing function
 $HashingFunction = { 
 0F BE 01 84 C0 74 39 31 D2 0F 1F 80 00 00 00 00
 01 D0 48 83 C1 01 89 C2 C1 E2 0A 01 D0 89 C2 C1
 EA 06 31 C2 0F BE 01 84 C0 75 E5 8D 14 D2 89 D0
 C1 E8 0B 31 D0 89 C2 C1 E2 0F 01 D0 C3 0F 1F 00
 31 C0 C3
 }
 //Obfuscation technique
 //Operation to check if the program is running in a debugger
 $AntiDebugViaPEBParsing = {
 65 48 8B 14 25 60 00 00
 00 0F B6 82 BC 00 00 00
 83 E0 70 3C 70
 }
 condition:
 all of them
}
rule Stage_BRC4_Part2 {
 meta:
 author = "Adams KONE, Airbus PROTECT"
 company = "Airbus PROTECT"
 sharing = "TLP:CLEAR"
 category = "MALWARE"
 description = "Stage of second part of shellcode"
 
 strings:
 //Obfuscation technique
 //API hashing function
 $HashingFunction = { 
 31 D2 0F BE 01 84 C0 74 14 01 D0 48 FF C1 69 C0
 01 04 00 00 89 C2 C1 EA 06 31 C2 EB E5 8D 04 D2
 89 C2 C1 EA 0B 31 D0 69 C0 01 80 00 00 C3
 }

//Obfuscation technique
 //Function to forge a pointer to syscall, ret instructions.
 $getAddrToJumpToSyscallAndRetInstruction = {
 48 89 C8 48 8D 51 14 80 38 0F 75 0C 80 78 01 05
 75 06 80 78 02 C3 74 0A 48 FF C0 48 39 C2 75 E7
 31 C0 C3
 }
 //Obfuscation technique
 //Hash of functions
 $InternetOpenW = { 2E 8F 43 C1 }
 $InternetConnectW = { E8 60 1F 7F }
 $InternetCloseHandle = { 43 30 5C 03 }
 $HttpOpenRequestW = { A9 2D 8A 74 }
 $InternetSetOptionW = { 25 04 40 7A }
 $HttpAddRequestHeadersW = { 35 25 AF A5 }
 $HttpSendRequestW = { 80 7B 17 E8 }
 $HttpSendRequestA = { 3A 79 F2 E6 }
 $HttpQueryInfoA = { 27 AF D2 5D }
 $InternetReadFile = { 46 CE FE BC }
 $InternetQueryDataAvailable = { AE D7 26 30 }
 
 condition:
 all of them
}

IOCs

  • 4400750cbc597b7e0cec813dcaf66d00e83955a034591a5a6ba40547a045721b
  • bd32cbb6c08eff7fc6aa0bfe2fd81ec467f70d9b726015859da39744271bbcb0
  • 780b2b715aa33e8910479a671469ad27cc88a7ed513b83e43cf7a6a16f613013
  • 206.166.251[.]128
  • 179.43.144[.]250
  • 213.215.163[.]51
  • Share