Incident Response: Analysis of recent version of BRC4 Published: 2024-10-22 · Archived: 2026-04-10 02:57:59 UTC 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: 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: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 1 of 15 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: After the execution with ScyllaHide plugin, we found a memory area with execution right and we jumped on it: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 2 of 15 During the analysis of this memory area, we finally found a main function, this function is our second stage: 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: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 3 of 15 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. 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 https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 4 of 15 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. 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: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 5 of 15 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: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 6 of 15 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: 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: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 7 of 15 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 sample 1 – 780b2b715aa33e8910479a671469ad27cc88a7ed513b83e43cf7a6a16f613013 sample 2 – 04b47b5492f5b2086e4a6b3f2bef73eb15a51140a86bcd05417d00bf6875ffb6 https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 8 of 15 sample 3 – 9ec67f1914603e729a3b6bafe3a96cdc660717ca7dfb457290f68fc56dd0a5e2 sample 4 – bd32cbb6c08eff7fc6aa0bfe2fd81ec467f70d9b726015859da39744271bbcb0 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", https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 9 of 15 "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: 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: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 10 of 15 [+] 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: Conclusion https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 11 of 15 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. https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 12 of 15 $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" https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 13 of 15 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 https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 14 of 15 Source: https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ https://www.protect.airbus.com/blog/incident-response-analysis-of-recent-version-of-brc4/ Page 15 of 15