Detailed analysis of Brute Ratel C4 1.2.2 Badger Shellcode

0 26
1. IntroductionThe Brute Ratel C4 version 1.2.2 was cracked and publicly leaked...

1. Introduction

The Brute Ratel C4 version 1.2.2 was cracked and publicly leaked on the internet. NinjaParanoid, the developer of Brute Ratel C4, posted on Twitter that it was MdSec who uploaded Brc4 to VT (VirusTotal), and then it was cracked by the Russian Molecules organization, causing the Brute Ratel C4 1.2.2 Scandinavian Defense to spread on the internet, and the Brute Ratel C4 1.2.5 leaked version to be circulated among a few people.
Detailed analysis of Brute Ratel C4 1.2.2 Badger Shellcode

MdSec is a security company from the United Kingdom, mainly engaged in penetration testing against simulation. MdSec developed Nighthawk C2 for sale, and without a doubt, MdSec's Nighthawk and Brute Ratel C4 are competitive products.
image

It can be seen that the new Nighthawk Licensing fee is 7500 pounds or 10000 US dollars per user per year, and it is necessary to purchase at least 3 user licenses.
image

2. Sample IOCs

Name: bruteratel-1.2.2-pwn3rzs-cyberarsenal.7z
Size: 82818265 bytes (78.9 MiB)
MD5: 756bf6d0e21d9e8247a08352cd38dffd
SHA1: 10ae61b605a51a71dc87abb9c28d1e2567d9b32e
SHA256: d5b0c42ef9642dce715b252a07fc07ad9917bfdc13bd699d517b78210cc6ec60

3. Malicious Code Analysis

The directory structure of the leaked Brc4 1.2.2 version is as follows:
image

The Bruteratel team server has two versions: x64 and arm64. We use the file command to view the server, and through Go BuildId, we can judge that it is written in golang.
image

commander-runme is the graphical interface of the client, and commander-runme is a shortcut to /lib64/commander.
image

Commander is a graphical interface client written using Qt.
image

To start the team server, use -a to specify the username, -p to specify the password, -h to specify the service port, -sc to specify the cert.pem certificate, and -sk to specify the key.pem private key.
image

After that, we start the client connection, Brc4 has only two types of Listeners: HTTP Listener and DOH Listener.
image

Here we create an HTTP Listener. In my local test, I directly used the IP address, and the port and User-Agent used the default configuration. I added 3 URLs, Sleep Obfuscation used the default APC method, the sleep time is default, and the Common Auth authentication password I chose is randomly generated. If you choose One Time Auth authentication password, after the current Auth authentication password is used once, it will be removed, which means it only supports logging in once. If you check the 'Die if C2 is inaccessible' option, if the connection to C2 fails, it will automatically exit.
image

We can open the Payload Profiler option Add Payload Profile, and also add TCP and SMB payloads. These two types of payloads cannot be directly connected to the Bruteratel server without first being forwarded through an already online badger. They are generally used in scenarios of internal network lateral movement within the same domain.
image

To use TCP or SMB payloads, there must be a badger host already online through HTTP or DOH within the domain. Then, use the pivot_tcp command to create a TCP port listener on the online badger host to connect with the TCP payload. Use the pivot_smb command to connect to the SMB payload through a named pipe. Any command executed on the TCP or SMB badger host will be forwarded to the already online HTTP or DOH badger host, which will then forward it to the Bruteratel team server. We can see that b-21 is the SMB badger and b-22 is the TCP badger, and both of these badgers are forwarded through the b-20 HTTP badger. These two types of badgers need to be forwarded through the already online badger to communicate with the Bruteratel server. They are generally used in scenarios of internal network lateral movement within the same domain.
image

Next, we generate the payload, and here I will first analyze the Shellcode RtlExitUserThread under the Default of x64 architecture:
image

3.1 badger_x64_rtl.bin

3.1.1 badger shellcode load

Next, we use the following shellcode load code to analyze the shellcode:

DWORD dwOldProtect = 0;
OVERLAPPED ol = { 0 };
HANDLE hFile = CreateFileW(L"shellcode.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
int fileSize = GetFileSize(hFile, NULL);
LPVOID lpShellCode = VirtualAlloc(NULL, fileSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
ReadFileEx(hFile, lpShellCode, fileSize, &ol, NULL);
CloseHandle(hFile);
VirtualProtect(lpShellCode, fileSize, PAGE_EXECUTE_READ, &dwOldProtect);
((void(*)())lpShellCode)();
WaitForSingleObject((HANDLE)-1, -1);

Using ida analysis, this is a characteristic of the shellcode start, after saving the register environment, through a large number ofmov reg, imm,push regA large amount of data is combined and initialized on the stack.
image

Firstly, initialize 0x12C bytes of base64-encoded Brc4 configuration file on the stack, the size of the configuration file varies according to the Listener's configuration
image

Next, initialize the 0x39410 bytes of encrypted data on the stack
image

Next, the encrypted data on the stack and the base64 configuration file data will both be copied to the applied heap space
image

The main logic of the MemMoveAllocHeap function is to obtain the base address of ntdll after getting the base address, obtain the address of the RtlAllocateHeap function through ror13hash, apply for heap space to copy data to the heap space.
image

The function GetNdllBaseAddress gets the address of the _PEB_LDR_DATA structure and searches for the MZSignature flag by brute force, then compares if AddressOfNewExeHeader-0x40 is less than 0x3BF and the NtSignature flag is PE, then the base address of ntdll.dll is found.
image

The address of the RtlAllocateHeap function is obtained through ror13hash, and heap space is applied for to copy data to the heap space
image

Copy 0x39410 bytes of encrypted data to the heap space
image

The configuration file data is also copied to the heap space
image

Next, enter the main function
image

Debugging prevention is performed through the NtGlobalFlang field at the address PEB+0xBC. If the process is created by a debugger, it will set (FLG_HEAP_ENABLE_TAIL_CHECK (0x10) | FLG_HEAP_ENABLE_FREE_CHECK (0x20) |
The FLG_HEAP_VALIDATE_PARAMETERS (0x40) flag is also 0x70, and the debugger attachment will not set this flag.
image

After obtaining the ntdll base address, the address of RtlFreeHeap function is obtained through ror13hash, and the syscall id and syscall address of ZwProtectVirtualMemory function are also obtained.
image

The CheckinlineHookAndGetSyscallId function detects whether the function header bytes are 0xCC (int3 software breakpoint), and also detects whether the 1st and 4th bytes are 0xE9 (jmp) to check if the function is inlineHook. If the current function detects inlineHook, it will continue to detect the previous Ntxxxx function.
image

If the detection passes, the syscall id is obtained by comparing the opcode features. The syscall id of Windows needs to be stored using a WORD type (2 bytes), so the 5th and 6th bytes are combined into a WORD type (2 bytes) here.
image

We check the syscall id table of NT10 (Windows 10/11), and we can see that the syscall id of the same function may change under different system version numbers, and the size of the syscall id occupied by different functions is different, so a WORD (2 bytes) is needed for storage.
image

The opcode features 0x0F05 (syscall) and 0xC3 (ret) of the Ntxxxx function are searched to obtain the syscall address.
image

Then, the addresses of LdrGetDllHandleEx and LdrGetProcedureAddress functions are obtained.
image

The GetDllAddrOrGetDllFullPathByHash function determines whether to obtain the dll base address or the UNICODE_STRING FullDllName field based on the flag passed in through parameter 3.
image

Based on parameter 3, if the parameter 3 is 0, the dll base address is returned; if the parameter 3 is 1, the UNICODE_STRING FullDllName field is returned.
image

Then, the LdrGetDllHandleEx function is called to load Kernel32.dll and obtain the base address, followed by obtaining the syscall id and syscall address of ZwFlushInstructionCache.
image

Next, the Rc4Decrypt function is called to decrypt encrypted data of 0x39410 bytes using the rc4 algorithm.
image

The Rc4Decrypt function first acquires the function address of RtlExitUserThread, GetProcAddress, and LoadLibraryA through ror13hash.
image

rc4的密钥在加密数据的尾部的8字节也就是This rc4 key is randomly generated}}
image

The rc4 key is in the last 8 bytes of the encrypted dataThis rc4 key is randomly generatedWe open CyberChef and use the rc4 key
image

After decrypting the encrypted data from the memory, it is found to be a PE file with the MZSignature flag removed
image

We use Exeinfo to view the PE file dump and it is a Mingw compiled x64 dll
image

We check the decrypted data in the memory after decryption in x64dbg and it is the same as the one decrypted using CyberChef
image

After rc4 decryption, the 8 bytes at the end of the PE file are encrypted, at this time, the first 8 bytes of the 16-byte header are the decrypted rc4 key of the Brc4 configuration filef?zi\)*<We use the rc4 key
image

After decrypting the Base64-encoded Brc4 configuration file, the decrypted Brc4 configuration file content can be seen
image

Next, call the ReflectiveDLLInjection function to invoke the decrypted badger core dll through reflective injection
image

Using the processhacker tool to view memory, we see that the virtual memory permission at 0x4D0000 has been changed to RW permission
image

Copy the first 0x400 bytes of the badger core dll in the heap to the RW permission memory at the address 0x4D0000, starting from here, we reuse the virtual memory applied for shellcode load to reflectively inject the badger core dll
image

Copy all sections of the badger core dll to RW memory
image

Fill the import table into RW memory
image

Fix the relocation data
image

Copy the Base64-encoded configuration file's rc4 key to the allocated heap space
image

Modify the memory permissions of each section
image

Release the heap memory used to store the decrypted badger core dll
image

Call ZwFlushInstructionCache to refresh the code cache
image

Then call the badger core dll main through call rax
image

3.1.2 badger core dll

In the main function of the badger core dll, the FreeConsole function is called to hide the console window, and some global variables used to store the dll base addresses are initialized.
image

The GetSomeDllFunAddress function retrieves the addresses of the functions needed in ntdll.dll, kernel32.dll, kernelbase.dll, advapi32.dll, crypt32.dll, and ws2_32.dll.
image

The function address in ntdll.dll is obtained through ror13hash
image

The GetWs2_32DllFunAddress function uses the rc4 keybYXJm/3#M?:XyMBFThe decryption of the ws2_32.dll string is obtained by the GetDllBaseAddress function to get the dll base address and by ror13hash to get the function address.
image

The GetDllBaseAddress function first calls the GetDllAddrOrFullPathByHash function to obtain the dll base address. If the acquisition fails, it will call the RtlRegisterWait function to execute the LoadLibraryA function through the worker thread of the thread pool, set the CALLBACKFUNC to the address of the LoadLibraryA function, set parameter 4 to the corresponding dll string to be loaded, and then wait for the event object execution through the WaitForSingleObject function. When the LoadLibraryA function loads the corresponding dll into the current process memory, it calls the GetDllAddrOrFullPathByHash function to obtain the base address of the corresponding dll loaded into memory.
image

The loading of dll through the RtlRegisterWait function is executed by the worker thread of the thread pool to execute the LoadLibraryA function, so it can achieve the effect of hiding the call stack, and can prevent EDR/AV from judging whether the call is malicious by tracing back the LoadLibraryA call stack.
image

Then, a thread is created through the SyscallZwCreateThreadEx function, the thread CONTEXT is obtained through the SyscallNtGetContextThread function, the thread execution function is set to the address of the ThreadMain function through the SyscallNtSetContextThread function, and the thread execution is resumed through the SyscallNtResumeThread function.
image

Next, let's look at the ThreadMain function
image

The initKeyAndFormatInfo function was called first
image

The initBrc4EncryptAlgorithmArray function initializes 9 arrays used by the custom encryption algorithm of Brc4, each array has 256 elements, and the initialization method is to reduce all elements within the array by 1. For the custom encryption algorithm of Brc4, you can go to mygithubCheck the algorithm I reverse-engineered.
image

We check the uninitialized ArrayBox1
image

The elements of the initialized ArrayBox1 are reduced by 1
image

Next, some formatting characters are initialized for displaying the basic information of the logged-in badger in the commander interface
image

After decoding Base64, use the rc4 keyf?zi\)*<Decrypt the Brc4 configuration file
image

The configuration file is through|The symbol (0x7C) is used for separation, so it will be judged with 0x7C and parsed field by field
image

After parsing, some fields will call the AsciiToHexadecimal function to convert the parsed configuration file fields from Ascii to hexadecimal
image

Next, the GetSystemInfo function retrieves the basic system information for the login package
image

The WSAStartup function initializes WinSock version 2.2
image

Next, the attribute of the memory address 0x511000 is queried
image

Let's take a look at the current memory, the virtual memory permission of 0x511000 is RX, this memory is used to run the code of the reflective injection badger core dll before
image

Change the memory attribute of 0x511000 to PAGE_READWRITE and clear the memory
image

Let's take a look at the current memory structure, the memory attribute of 0x511000 is changed to PAGE_READWRITE and the memory is cleared
image

Next, send the login package to C2 first
image

Firstly, obtain the address of the exported function of Wininet.dll through the ror13 hash method
image

Format the login package
image

Encrypt the login package using Brc4's custom encryption algorithm
image

After encryption, the login package is encoded using Base64
image

Then, the encrypted login package will be sent to C2
image

Firstly, set User-Agent
image

Set the domain name and port
image

Set the request method and request path
image

Send a POST request
image

Query whether there are tasks in the C2 task queue
image

Read the data returned by the C2 server if there are tasks in the task queue
image

base64 decoding
image

Decrypted using Brc4's custom encryption algorithm, the data returned here is used for heartbeat packets, b-xx represents the number of badgers logged in on the current Listeners, starting from b-0\\The following are b-cookie, these two data are used to determine the uniqueness of the currently logged in badger
image

After that, the initCallbackFun function is called to initialize the built-in exploit command function call table of Brc4, using 2 arrays to store function call addresses and function call IDs in sequence
image

This is the built-in exploit command and corresponding function call ID of Brc4, excluding the 3 commands (help, cls, title) used for the C2 Commander graphical interface, there are a total of 131 commands for interacting with Badger, and the function call IDs of 2 commands are the same

call IDcommand
0x3C9Fpwd
0xD53Farp
0x4FFEuserinfo
0x391lockws
0x609lsdr
0xA01uptime
0xB06idletime
0x703exit_process
0x605revtoken
0x105dumpclip
0xC144drivers
0x2919list_downloads
0xA217get_parent
0x1719set_debug
0x4318tasks
0x5921get_child
0x6154psclean
0x9C41screenshot
0xBA9Dlist_tcppivot
0xA63Cclear_parent
0x3BA8clear_child
0x71C6get_argument
0x93D6clear_argument
0xE3CBdcenum
0x8289get_malloc
0x8146get_threadex
0xF616ipstats
0x4A9Edll_block
0xB3E4dll_unblock
0x3793get_wmiconfig
0x2698reset_wmiconfig
0x44B7token_vault
0xED33vault_clear
0x803exit_thread
0x4395get_killdate
0x4934shadowcloak
0xB339netstat
0xD41Aroutes
0xBE9Alocal_sessions
0x38B7dnscache
0xB6A3getenv
0x5248sysinfo
0x6135windowlist
0x73E8applist
0xD9A3crisis_monitor
0xD359socks_start, socks_profile_start
0xD959socks_stop
0x2DA1keylogger
0x2129sleep
0x1139cd
0xA905cp
0x9B84mv
0xE993rm
0x3F61mkdir
0x8F40rmdir
0xA32ls
0xA959net
0xF584runas
0xF999make_token
0xE9B0run
0xEBC0kill
0xBED0shellspawn
0x9DE0ps
0x8AFAset_parent
0x6BAEget_system
0x6F39system_exec
0xF3D9psreflect
0x3FD4loadr, mimikatz
0x2C74download
0x6C36reg
0xC929set_child
0xB458scquery
0xE2EApsimport
0x13A1upload
0x699Apivot_tcp
0x73E6set_argument
0x3C4Dpivot_smb
0xFE37psexec
0x97E9sccreate
0xFA73scdelete
0x3B3Escdivert
0x5962set_malloc
0x5761set_threadex
0xC662psgrep
0xE591portscan
0x9881dcsync
0x4953netshares
0x4355set_wmiconfig
0x5213wmiquery
0x81E7grab_token
0xF856impersonate
0xCB46vault_remove
0x4932coffexec
0x6492list_modules
0x2133memhunt
0x7348suspended_run
0x8491set_killdate
0x8044sharpinline
0x3456scstart
0xB98Equery_session
0x7579sentinel
0xB99Apasspol
0xB69Aschtquery
0x29B3sharescan
0xE4A9shinject_ex
0xD8F3ps_ex
0xB6BFswitch_profile
0xB3A9timeloop
0xE19Apreview
0xA657lookup
0xA5F1memdump
0xD163addpriv
0xE53Afileinfo
0xB1D3wmiexec
0xF83Elstree
0xE4B9kerberoast
0xB93Aicmp_ping
0xDA9Cphish_creds
0x34AEstart_address
0xCDE4threads
0xE1BAphantom_thread
0xF2EDstop_task
0xD2E5obfsleep
0x4A83socks_profile
0x3BD8memhook
0xA23Bsamdump
0xE3D2sharpreflect
0xA7D9set_coffargs
0xD8A9clear_coffargs

Enter the help command in the C2 Commander graphical interface to obtain all built-in commands, but there is no command corresponding to 0xF83E. By reverse engineering brute-ratel-linx64, which is the TeamServer end of C2, the corresponding command is lstree, which may still be under development and cannot be used
image

Format the heartbeat packet with the unique ID data returned by C2
image

Use Brc4 custom encryption algorithm to encrypt the heartbeat packet
image

Use base64 encoding
image

Send a heartbeat packet to C2
image

After entering the sleep obfuscation function, use the SystemFunction036 function to generate a 16-byte pseudo-random number for encrypting the rc4 key of the heap space data
image

The Rc4CryptDecryptHeapData function encrypts all the heap space data used with the randomly generated rc4 key
image

Select the corresponding sleep obfuscation function according to the sleep obfuscation method selected in the configuration file, we configure it as APC mode
image

Create a fiber execution sleep obfuscation function
image

Switch to fiber execution
image

Firstly, allocate heap space of the size of the CONTEXT structure (0x4D0) for the ROP chain, and use SystemFunction036 to generate a 16-byte pseudo-random number for encrypting the rc4 key of the badger core dll memory in sleep.
image

Then call the SetProcessValidCallTargets function to disable the CFG (Control Flow Guard) protection for several functions used by the ROP chain, which was included in the Windows 8.1 UPDATE (KB3000850) patch and can prevent the indirect execution of arbitrary code in the program. Enabling the /guard:cf flag in the VS compiler can enable it.
image

In self-written load programs, CFG protection is generally not enabled during compilation. However, considering that shellcode needs to be injected into the system process space for execution, and system processes are mostly compiled with CFG protection enabled, compatibility considerations require that the CFG protection of the functions used in the ROP chain be disabled.
image

An Event object is created, a suspended thread is created, and the address of TpReleaseCleanupGroupMembers+0x450 is set as the thread starting address. The ContextFlags of the ROP chain CONTEXT structure are filled with CONTEXT_FULL (0x10000B).
image

The CONTEXT of the created suspended thread is obtained and copied to the CONTEXT of the ROP chain.
image

Then, a fake thread stack context is constructed using the NtWaitForWorkViaWorkerFactory, BaseThreadInitThunk, and RtlUserThreadStart functions.
image

Subsequently, the CONTEXT structure contexts of the ROP chain functions ZwWaitForSingleObject, ZwProtectVirtualMemory, SystemFunction032, and ZwGetContextThread are filled. The ZwTestAlert function is used to immediately execute the suspended APC callback in the thread APC queue.
image

The CONTEXT structure contexts of the ROP chain functions ZwSetContextThread, WaitForSingleObjectEx, SystemFunction032, and ZwGetContextThread are filled.
image

The NtQueueApcThread function is used to create an APC queue, with the ZwContinue function as the callback function to execute the ROP chain CONTEXT inserted into the APC queue. The ZwAlertResumeThread function is then used to resume thread execution, and the last block of heap memory is encrypted using the rc4 algorithm. The NtSignalAndWaitForSingleObject function waits for the current process while maintaining unalertability through a notification event.
image

The prototype of the ZwContinue function is as follows, with the first parameter being a pointer to the CONTEXT structure.
image

ROP chain starts executing at this point, with RIP pointing to the ZwContinue function and RCX pointing to the ZwWaitForSingleObject function's CONTEXT structure. ZwWaitForSingleObject waits for the current process object as an unalertable object.
image

NtProtectVirtualMemory modifies the current badger core dll memory permission to PAGE_READWRITE.
image

SystemFunction032 encrypts the badger core dll memory using the previously randomly generated 16-byte pseudo-random number with the rc4 algorithm
image

ZwGetContextThread retrieves the current executing ROP chain thread's CONTEXT structure context
image

ZwSetContextThread sets the fake call stack to the ROP chain thread.
image

Sleep and wait using the sleep time set in the configuration file through WaitForSingleObjectEx.
image

When the badger core dll is in sleep, the memory permission is changed to RW and all are encrypted.
image

The call stack of the欺骗 thread has a fixed function offset, RtlUserThreadStart+0x21, BaseThreadInitThunk+0x14, TpReleaseCleanupGroupMembers+0x747, ZwWaitForWorkViaWorkerFactory+0x14.
image

After the sleep is completed, the SystemFunction032 function is called to decrypt the badger core dll memory using the rc4 algorithm.
image

The NtProtectVirtualMemory function changes the memory permission of the badger core dll to PAGE_EXECUTE_READ.
image

ZwSetContextThread restores the CONTEXT structure context of the ROP chain thread.
image

RtlExitUserThread exits the current thread and ends the ROP chain.
image

After the operation is completed, release the heap space of the ROP chain, close the handle, and then switch to Fiber using the SwitchToFiber function.
image

Brc4 supports 3 types of sleep obfuscation methods. We can switch the sleep obfuscation method using the obfsleep command. obfsleep 0 is the APC method analyzed just now, obfslep 1 and obfsleep 2 correspond to Poling-0 and Poling-1, respectively. Next, we will analyze the sleep obfuscation of the Poling method. First, allocate heap space for the ROP chain's CONTEXT structure (0x4D0 bytes) and use the SystemFunction036 function to generate a 16-byte pseudo-random number for encrypting the rc4 key of the badger core dll memory during sleep.
image

Then disable the CFG protection of the functions used in the ROP chain and create an Event object. Here, the IsPoling flag is used to determine whether RtlCreateTimer function is used when obfslep 1 is used, or RtlRegisterWait function is used when obfsleep 2 is used. The callback function RtlCaptureContext is used to obtain the CONTEXT structure of RtlCreateTimer or RtlRegisterWait function.
image

Then copies the CONTEXT structure context obtained by the RtlCaptureContext callback to the CONTEXT of the ROP chain
image

Constructs a false thread call stack CONTEXT structure context using the NtWaitForWorkViaWorkerFactory, BaseThreadInitThunk, RtlUserThreadStart functions
image

Then fills the CONTEXT structure context of the ROP chain functions VirtualProtect, SystemFunction032, ZwGetContextThread, ZwSetContextThread, WaitForSingleObject, RSP-8 is used to adjust the offset caused by the RtlCaptureContext callback function call
image

Fills the CONTEXT structure context of the ROP chain functions ZwSetContextThread, SystemFunction032, VirtualProtect, NtSetEvent
image

Judges the choice of creating a timer queue with RtlCreateTimer function or registering a wait handle with RtlRegisterWait function to create the ROP chain through the IsPooling flag, uses ZwContinue as the callback function to execute the ROP chain's CONTEXT, the parameter 5 is used to adjust the time interval between ROP chain calls, then encrypts the last block of heap space data with the rc4 algorithm, and waits for the Event object with WaitForSingleObject.
image

The VirtualProtect function changes the memory permissions of the badger core dll to PAGE_READWRITE
image

SystemFunction032 encrypts the badger core dll memory using the previously randomly generated 16-byte pseudo-random number with the rc4 algorithm
image

ZwGetContextThread retrieves the current executing ROP chain thread's CONTEXT structure context
image

ZwSetContextThread sets a false call thread stack to the ROP chain thread
image

Sleeps for the duration specified in the configuration file using WaitForSingleObject
image

ZwSetContextThread restores the execution of the current ROP chain thread's CONTEXT structure context
image

The SystemFunction032 function decrypts the badger core dll memory using the rc4 algorithm
image

The VirtualProtect function changes the memory permissions of the badger core dll to PAGE_EXECUTE_READ
image

The ZwSetEvent function sets the state of the Event object to Signaled
image

After the end, release the ROP chain memory, close the handle, and switch back to Fiber
image

Next, analyze the post-exploitation command call of Brc4, we use the mkdir testdir command to test. After sending the heartbeat packet to C2, we query if there is a task in the task queue. If there is a task, it will be parsed. The first layer is base64 encoding, the second layer is decrypted by the Brc4 custom encryption algorithm, and the third layer is base64 decoding, which becomes the actual command call data
image

We check the decrypted data, 0x3f61 is the function call ID of the mkdir command, 0x20 is the delimiter, and testdir is the parameter of our mkdir command
image

Next, copy the function call ID and parameters separately to the heap space
image

Create a task thread to execute the command
image

Enter TastThread and call the CheckCurrentCallbackFunid function to check if the current function call ID is at the index of the current function call ID table
image

The function call ID occupies 2 bytes, so the current function call ID and the function call ID in the current index of the function call table are compared separately. If both bytes are the same, the function returns TRUE, indicating a successful comparison
image

If the current function call ID is not at the index of the current function call ID table, the index will be incremented by 1, and the address of the function call ID table array will be incremented by 3, and the next function call ID in the function call ID table will be compared in a loop
image

If the ID comparison is successful, make the call throughcall qword ptr [rax+rsi*8]Make the call, where rax is the first address of the function call address table, and rsi is the index of our current function call ID in the function call ID table
image

Enter the mkdir function and use the CreateDirectoryA function to create the testdir file
image

If the folder is created successfully, format the return information string
image

The formatted return message is encoded with base64 and then encrypted to wait for sending the data to C2
image

We check the information returned by the COMMAND interface
image

3.2 badger_x64_stealth_rtl.bin

In the previous section, we analyzed the shellcode of the Exit Method: RtlExitUserThread under Default and the badger core dll payload. Next, we analyze the Exit Method: RtlExitUserThread shellcode under Stealth. We mainly analyze the differences of Stealth and the code that is the same as Default, which will not be analyzed again. Compared to Default's shellcode, Stealth mainly adds the FixDllMemoryHook function
image

Get the UNICODE_STRING FullDllName of kernelbase.dll
image

Get the UNICODE_STRING FullDllName of ntdll.dll
image

After calling the ReadDiskDllFixMemoryHook function, first\??\Concatenated with FullDllName
image

The NtOpenFile function opens the dll file handle
image

If the file is opened successfully, call the NtReadFile function to read the dll into the allocated heap
image

After parsing the PE file to obtain the .text section addresses of disk dll and memory dll, and changing the memory permission of the .text section of the memory dll to PAGE_EXECUTE_READWRITE
image

Cover the .text section of the disk dll with the .text section of the memory dll
image

After changing the memory permission of the .text section of the memory dll to PAGE_EXECUTE_READ, call the GetDiskDllRdataSectionInfo function
image

The function GetDiskDllRdataSectionInfo gets the .rdata section addresses of disk dll and memory dll by parsing the PE file
image

Change the memory permission of the .rdata section of the memory dll to PAGE_READWRITE
image

Cover the .rdata section of the disk dll with the .rdata section of the memory dll
image

Then call ReadDiskDllFixMemoryHook on kernel32.dll and kernelbase.dll for UnHook
image

Then call ZwFlushInstructionCache to refresh the code cache
image

Compared to the Default shellcode, Stealth mainly adds a FixDllMemoryHook function, which mainly reads the .text section and .rdata section of ntdll.dll, kernel32.dll, and kernelbase.dll from the hard disk, and covers the .text section and .rdata section in memory that may be hooked by security software such as AV or EDR
image

3.3 stage_x64_rtl.bin

The features of the stage shellcode analyzed by ida are the same as those of the badger shellcode
image

Encrypted data of size 0xC4 initialized on the stack, with the last 8 bytes being the rc4 key
image

Decryption with CyberChef reveals it to be a configuration file
image

Both the payloads of stage and stealth have the FixDllMemoryHook function used for Unhooking ntdll.dll, kernel32.dll, and kernelbase.dll
image

Use the rc4 algorithm keybYXJm/3#M?:XyMBFDecrypt the wininet.dll string and then call the GetDllBaseAddressRtlRegisterWait function to load the dll and obtain the base address
image

Firstly, create the Event object
image

The RtlRegisterWait function calls the LoadLibraryA function through the worker threads of the thread pool, with parameter 3 being the address of LoadLibraryA and parameter 4 being the wininet.dll string
image

Then wait for the Event object with WaitForSingleObject
image

Call the GetDllBaseAddressOrGetDllFullPath function to obtain the base address of winnet.dll loaded into memory
image

Then obtain the addresses of the functions needed in winnet.dll through ror13hash
image

Use the rc4 algorithm keybYXJm/3#M?:XyMBFDecrypt the crypt.dll string and then load the base address of the dll
image

Then obtain the address of the CryptBinaryToStringA function through ror13hash
image

Use the rc4 algorithm key>s?un&>)The decryption of the configuration file is the same as the one we just decrypted with CyberChef, and this rc4 key is randomly generated
image

Then call the ParsingStageConfiguration function to parse each field of the configuration file individually
image

After parsing the configuration file, call the GetHttpBadgerShellcode function to obtain the next stage Badger Shellcode
image

Format the authentication package through the auth key in the configuration file
image

Use the rc4 algorithm key>s?un&>)Encrypt the authentication package
image

Use base64 encoding to encrypt the authentication package
image

Call the InternetOpenA function to set User-Agent
image

InternetConnectA sets the domain name and port
image

HttpOpenRequestA sets the request method and path
image

HttpAddRequestHeadersA attaches the rc4 algorithm encryption key to the HTTP request header
image

The HttpSendRequestA function sends the specified request to the C2 server
image

The InternetQueryDataAvailable function queries the size of the data returned by the C2 server
image

InternetReadFile reads the data returned by the C2 server
image

Use the rc4 algorithm key>s?un&>)Decrypt the data returned by the C2 server
image

The ZwAllocateVirtualMemory function is used to allocate virtual memory with PAGE_READWRITE permissions
image

The Badger Shellcode is copied to the newly allocated virtual memory
image

ZwProtectVirtualMemory is called to change the memory permissions of the Badger Shellcode to PAGE_EXECUTE_READ
image

The Badger Shellcode is called, as the Badger Shellcode has been analyzed before, so there is no need to continue the analysis here
image

Using BinDiff, it is found that the shellcode obtained by stage is badger_x64_stealth_ret
image

3.4 badger_x64.dll

Using Exeinfo, it can be seen that badger_x64.dll is an x64 dll compiled with MinGW-w64 and also has a large .data section
image

Next, analyzing the dll main, mainly through ror13hash to obtain the base address of ntdll, then obtain the syscall id and syscall address of NtProtectVirtualMemory, ZwAllocateVirtualMemroy, ZwWaitForSingleObject, ZwCreateThreadEx functions
image

ZwAllocateVirtualMemory allocates virtual memory with PAGE_READWRITE permissions for the shellcode, copies the shellcode in the .data section to the allocated virtual memory, clears the shellcode in the .data section, and then changes the virtual memory permissions of the shellcode to PAGE_EXECUTEREAD
image

We can see that the Badger Shellcode in the .data section is not encrypted at all
image

After ZwCreateThreadEx creates a thread to execute Badger Shellcode, it can be concluded here that badger_x64.dll is actually a loader used to load Badger Shellcode
image

Using BinDiff, it can be seen that the shellcode used by badger_x64.dll is badger_x64_ret
image

3.5 badger_x64_svc.exe

Using Exeinfo, it can be seen that badger_x64_svc.exe is also a 64-bit program compiled with MinGW-w64 and also has a large .data section
image

Here, the base address of ntdll.dll is obtained not through ror13hash but directly through brute force search
image

Then, obtain the syscall id and syscall address of the functions NtProtectVirtualMemory, ZwAllocateVirtualMemroy, ZwWaitForSingleObject, and ZwCreateThreadEx through ror13hash.
image

Next, the same operation is performed: copy the Badger Shellcode from the .data section to the allocated virtual memory, clear the shellcode in the .data section, change the virtual memory permissions to PAGE_EXECUTEREAD, and then call ZwCreateThreadEx to create a thread and execute.
image

Using BinDiff comparison, it is found that the shellcode used by badger_x64_svc.exe is also badger_x64_ret.
image

3.6 badger_x64_stealth_svc.exe

Using Exeinfo, it is found that badger_x64_stealth_svc.exe is a 64-bit program compiled with MinGW-w64, and it also has a large .data section. According to this feature, it can be known that the main logic also loads and executes the shellcode from the .data section.
image

The main logic of badger_x64_stealth_svc is the same as that of badger_x64_svc, and no further elaboration is needed.
image

After comparing with BinDiff, it is found that the shellcode used by badger_x64_stealth_svc is badger_x64_stealth_ret.
image

4. Summary

This article analyzes in detail all the Badger Payloads of the x64 architecture of the leaked Brute Ratel C4 Scandinavian Defense 1.2.2 HTTP Listener. As of the time of writing, Brc4 has been updated to the 1.4 Resurgence version. The developers have improved the Bruteratel team server and shellcode core, such as removing the always-used ror13 hash algorithm, changing the storage method of the configuration file from base64 encoding to binary, and removing the rc4 key used to decrypt dll strings in the badger core dll.bYXJm/3#M?:XyMBFThe deletion of all command output strings used in the badger core dll will be directly formatted and output by the Bruteratel server, etc. Due to space limitations, the next article will introduce the detection of badger core dll and Bruteratel team server in sleep mode.

你可能想看:

Distributed Storage Technology (Part 2): Analysis of the architecture, principles, characteristics, and advantages and disadvantages of wide-column storage and full-text search engines

EMOTET banking trojan is still active: shellcode release methods, infrastructure updates, and traffic encryption

(3) Is the national secret OTP simply replacing the SHA series hash algorithms with the SM3 algorithm, and becoming the national secret version of HOTP and TOTP according to the adopted dynamic factor

In-depth Analysis and Practice: Analysis of Apache Commons SCXML Remote Code Execution Vulnerability and POC EXP Construction

Analysis of a Separated Storage and Computing Lakehouse Architecture Supporting Multi-Model Data Analysis Exploration (Part 1)

As announced today, Glupteba is a multi-component botnet targeting Windows computers. Google has taken action to disrupt the operation of Glupteba, and we believe this action will have a significant i

2021-Digital China Innovation Competition-Huifu Cybersecurity Track-Final-Web-hatenum and source code analysis and payload script analysis

b) It should have a login failure handling function, and should configure and enable measures such as ending the session, limiting the number of illegal login attempts, and automatically logging out w

HTTP data packets & request methods & status code judgment & brute force encryption password & exploiting data packets

b) It should have the login failure handling function, and should configure and enable measures such as ending the session, limiting the number of illegal logins, and automatically exiting when the lo

最后修改时间:
admin
上一篇 2025年03月28日 10:15
下一篇 2025年03月28日 10:37

评论已关闭