LummaC2

Lumma Stealer: A Point-of-entry Analysis
Sample sent SMTP to our unpac.me honeypot.

LummaC2 Stealer is a new, premium-priced native/C++ credential harvesting commercial malware marketed to novice threat actors I am projecting to rapidly increase in popularity in penetrating the threat landscape. Analysis has been conducted on Lumma Stealer’s Point-of-entry, behavioral characteristics and C2 evasion methodology on [Date].

 

Malvertising via .rar with password

LummaC2 discovery campaign being specifically analyzed:

SMTP e-mail blast of an .html letter to inbox yielded a spear-phish attempt on Walmart Marketplace sellers through a geo-fenced url re-direct to appropriate geo-specific landing page.

The Phishing Sample

This recent, novel, phishing attempt mimicked communications from Walmart, specifically targeting sellers on the Walmart Marketplace. The deceptive email requested sellers to verify their contact details through a file named “Walmart Brand Portal.rar.” Notably, the email advised disabling antivirus software if the download encountered issues, a classic red flag indicating malicious intent.

We took bets whether or not it would be the common .lnk, .img, .exe, .bat or .ps1.

We are thankful for the costco-style free malware sample.

Dynamic Triage Methodology

 

When analyzing multi-faceted drop malware, particularly quick credential stealing malware, the focus often shifts to extracting the final payload rather than dwelling on the initial loader binary. Each analyst employs unique strategies for isolating and dumping the target malware piece.

I’ll share my preferred tool for swiftly accessing the final payload: PE-Sieve, a powerful utility designed by hasherezade. PE-Sieve’s capabilities encompass scanning processes, recognizing various potentially malicious implants, and dumping them for further analysis.

PE-Sieve ; “Scans a given process. Recognizes and dumps a variety of potentially malicious implants (replaced/injected PEs, shellcodes, hooks, in-memory patches).”

Dumping Lumma Binary

To extract the Lumma binary, I execute the executable contained within the phishing email’s archive. Using Process Hacker, I monitor the executable’s behavior, waiting for specific internal processes—like AddInProcess32.exe—to be created as part of the injection process.

  • Execute the payload.
  • Utilize PE-Sieve on the Injected Process.

Step 1 – Execute the payload:

Step 2 – Utilizing PE-Sieve on the Injected Process:

 

Exploring Lumma’s Functionality

With the extracted payload in hand, I delve into Lumma Stealer’s functionalities and capabilities.

Control Flow Flattening

Lumma’s developers implemented Control Flow Flattening (CFF) within the stealer code, complicating reverse engineering efforts. This obfuscation technique, poses challenges for malware reversers, highlighting the evolving tactics employed by threat actors.

 

Luckily, with this type of obfuscation; even though it is complex, it is not unique. We are going to un-flatten the dump, similar to my Emotet unpacking utilizing the power of SophosLabs’ POC.

 

This allows us to parse and read the data at-a-glance to organize the threat flow of functions.

String Obfuscation

As I’ve mentioned before in previous write-ups, native string obfuscation within malware does nothing but provide access to write specific YARA rules and have the malware subject to heuristic, unique analysis. The core principle of malware is to be illusive and as un-identifiable as possible (blending in with other Sysinternals). Yet, commercially sold malware developers are so afraid to have their binarys reversed and released for free/replicated that they’re actually helping reverse engineers deteriorate the efficiency of their product.

 

An initial inspection of extracted payload strings revealed extensive obfuscation, particularly marked by the recurring string “576xed” inserted within them. Lumma employs a dedicated deobfuscation function to unravel these strings, a process I automated using a Python script to extract and deobfuscate strings efficiently.
Somewhat disappointing obfuscation likely only to deter novice reverse engineers.

 

 

Script:

import idc
import idautils

DECRYPTION_FUNCTION = 0x45DF86 # Change to the relevant function call
STRINGS_FILE_PATH = '' # Output file for the strings
OBFUSCATOR_STRING = '576xed' # Might be changed in future builds

def getArg(ref_addr):
    ref_addr = idc.prev_head(ref_addr)
    if idc.print_insn_mnem(ref_addr) == 'push':
        if idc.get_operand_type(ref_addr, 0) == idc.o_imm:
            return(idc.get_operand_value(ref_addr, 0))
        else:
            return None

stringsList = []

for xref in idautils.XrefsTo(DECRYPTION_FUNCTION):
    argPtr = getArg(xref.frm)
    if not argPtr:
        continue
    data = idc.get_bytes(argPtr, 100)
    obfuscatedData = data.split(b'\x00\x00')[0].replace(b'\x00',b'').decode()
    stringsList.append(obfuscatedData.replace(OBFUSCATOR_STRING,""))

print(f'[+] {len(stringsList)} Strings were extracted')

out = open(STRINGS_FILE_PATH, 'w')
for string in stringsList:
    out.write(f'{string}\n')
out.close()
[+] 135 Strings were extracted

################
# OUTPUT FILE: #
################

\Local Extension Settings\
/Extensions/
*
nkddgncdjgjfcddamfgcmfnlhccnimig
NeoLine
cphhlgmgameodnhkjdmkpanlelnlohao
Clover
nhnkbkgjikgcigadomkphalanndcapjk
Liquality
kpfopkelmapcoipemfendmdcghnegimn
Terra Station
fhmfendgdocmcbmfikdcogofphimnkno
Auro
cnmamaachppnkjgnildpdmkaakejnhae
aeachknmefphepccionboohckonoeemg
Authenticator
bhghoamapcdpbohphigoooaddinpkbai
Cyano
dkdedlpgdmmkkfjabffeganieamfklkm
Byone
Login Data For Account
OneKey
Nifty
jbdaocneiiinmjbjlgalhcelgbejmnid
Math
iWlt
kncchdigobghenbbaddojjnnaogfppfj
EnKrypt
kkpllkodjeloidieedojogacfhpaihoh
Wombat
amkmjjmmflddogmhpjloimipbofnfjih
MEW CX
nlbmnnijcnlegkjjpcfjclmcfggfefdm
Guild
nanjmdknhkinifnkgdcggcfnhdaammmj
Coin98
infeboajgfhgbjpjbeppbkgnabfdkdaf
Leaf
cihmoadaighcejopammfbmddcmdekcje
Authy
ejbalbakoplchlghecdalmeeeajnimhm
nkbihfbeogaeaoehlefnkodbefgpgknn
TronLink
ibnejdfjmmkpcnlpebklmnkoeoihofec
Ronin Wallet
fnjhmkhhmkbjkkabndcnnogagogbneec
Binance Chain Wallet
fhbohimaelbohpjbbldcngcnapndodjp
Yoroi
afbcbjpbpfadlkmhmclhkeeodmamcflc
gaedmjdfmmahhbjefcbgaolhhanlaolb
Saturn
bcopgchhojmggmffilplmbdicgaihlkp
ZilPay
klnaejjgbibmhlephnhpmaofohgkpgkd
Phantom
bfnaelmomeimhlpmgjnjophhpkkoljpa
hcflpincpppdclinealmandijcmnkbgn
Temple
ookjlbkiijinhpmnjffcofjonbfbgaoc
TezBox
mnfifefkajgofkcjkemidiaecocnkjeh
DAppPlay
lodccjjbdhfakaekdiahmedfbieldgik
BitClip
ijmpgkjfkbfhoebgogflfebnmejmfbml
Steem Keychain
History
Jaxx Liberty
cjelfplplebdjjenllpjcblmjkfcffne
BitApp
fihkakfobkmkjojpchpfgcmhfjnmnfpi
Network\Cookies
History
Polymesh
jojhfeoedkpkglbfimdfabpdfjaoolaf
ICONex
flpiciilemghbmfalicajoolhkkenfel
Nabox
ffnbelfdoeiohenkjibnmadjiehjhajb
Web Data
Login Data
aiifbnbfobpmeekipheeijimdpnlpgpp
Keplr
dmkamcknogkgcdfhhbddcghachkejeap
Sollet
nlgbhdfgdhgbiamfdfmbikcdghidoadd
Coinbase
hnfanknocfeofbddgcijnmhnfnkdnaad
Guarda
hpglfhgfnhbgpjdenjgmdgoeiappafln
EQUAL
blnieiiffboillknjnepogjhkgnoapac
lkcjlnjfpbikmcmbachjpdbijejflpcm
Nash Extension
onofpnbbkehpmmoabgpcpmigafmmnjhl
Hycon Lite Client
Trezor Password Manager
imloifkgjagghnncjkhggdhalmcnfklk
EOS Authenticator
oeljdldpnmdbchonielidgobddffflal
GAuth Authenticator
ilgcnhelpchnceeipipijaljkblbcobl
nknhiehlklippafakaeklbeglecifhad
KHC
\Local State
*.txt
%userprofile%
Wallets/Ethereum
keystore
%appdata%\Ethereum
%localappdata%\Kometa\User
Chromium
%localappdata%\Chromium\User Data
Edge
%localappdata%\Microsoft\Edge\Us
%appdata%\Opera Software\Op576xe
Chrome
%localappdata%\Google\Chro
Mozilla Firefox
Wallets/Binance
app-store.json
%appdata%\Binance
Kometa
Important Files/Profile
Opera GX Stable
%appdata%\Opera Software\Op576xe
Opera Neon
Opera Stable
%appdata%\Opera Software\Op576xe
Wallets/Electrum
*
%appdata%\Electrum\wallets
%appdata%\Mozilla\Firefox\Prof57
System.txt

 

Google Chrome Extensions Targeted

 

Analyzing the extracted strings, I noticed Lumma’s focus on Chrome extension IDs, specifically those 32-character lower-case strings.  (Example: ilgcnhelpchnceeipipijaljkblbcobl) those strings are actually CRX IDs (Chrome Extension ID) which by navigating to C:\Users\User\AppData\Local\Google\Chrome\User Data\Default\Extensions These CRX IDs facilitate Lumma in identifying existing extensions on victims’ computers, aiding in the exfiltration of sensitive extension data. 

 

This script will cross-reference CRX IDs with the google chrome store:

import re, requests

LUMMA_STRINGS = '/Users/igal/malwares/Lumma/29.03.2023/LummaStrings.txt'
REGEX_PATTERN = '^[a-z]{32}$'
strings = open(LUMMA_STRINGS,'r').read()
crxExtensionList = re.findall(REGEX_PATTERN,strings,re.MULTILINE)


for crxId in crxExtensionList:
    url = f'https://chrome.google.com/webstore/detail/{crxId}'
    responseText = requests.get(url).text
    try:
        startIndex = responseText.index('itemprop="name" content="') + len('itemprop="name" content="')
        endIndex = responseText.index('"', startIndex)
        extensionName = responseText[startIndex:endIndex]
        print(f'[+] Extension name:{extensionName} , CRX ID:{crxId}')
    except ValueError:
        continue
[+] Extension name:NeoLine , CRX ID:cphhlgmgameodnhkjdmkpanlelnlohao
[+] Extension name:CLV Wallet , CRX ID:nhnkbkgjikgcigadomkphalanndcapjk
[+] Extension name:Liquality Wallet , CRX ID:kpfopkelmapcoipemfendmdcghnegimn
[+] Extension name:Auro Wallet , CRX ID:cnmamaachppnkjgnildpdmkaakejnhae
[+] Extension name:Coin98 Wallet , CRX ID:aeachknmefphepccionboohckonoeemg
[+] Extension name:Authenticator , CRX ID:bhghoamapcdpbohphigoooaddinpkbai
[+] Extension name:Cyano Wallet , CRX ID:dkdedlpgdmmkkfjabffeganieamfklkm
[+] Extension name:iWallet , CRX ID:kncchdigobghenbbaddojjnnaogfppfj
[+] Extension name:Enkrypt: Ethereum, Polkadot & Canto Wallet , CRX ID:kkpllkodjeloidieedojogacfhpaihoh
[+] Extension name:Wombat - Gaming Wallet for Ethereum & EOS , CRX ID:amkmjjmmflddogmhpjloimipbofnfjih
[+] Extension name:MEW CX - is now Enkrypt , CRX ID:nlbmnnijcnlegkjjpcfjclmcfggfefdm
[+] Extension name:LeafWallet - Easy to use EOS wallet , CRX ID:cihmoadaighcejopammfbmddcmdekcje
[+] Extension name:MetaMask , CRX ID:nkbihfbeogaeaoehlefnkodbefgpgknn
[+] Extension name:TronLink , CRX ID:ibnejdfjmmkpcnlpebklmnkoeoihofec
[+] Extension name:Ronin Wallet , CRX ID:fnjhmkhhmkbjkkabndcnnogagogbneec
[+] Extension name:Binance Wallet , CRX ID:fhbohimaelbohpjbbldcngcnapndodjp
[+] Extension name:Math Wallet , CRX ID:afbcbjpbpfadlkmhmclhkeeodmamcflc
[+] Extension name:Authy , CRX ID:gaedmjdfmmahhbjefcbgaolhhanlaolb
[+] Extension name:Hycon Lite Client , CRX ID:bcopgchhojmggmffilplmbdicgaihlkp
[+] Extension name:ZilPay , CRX ID:klnaejjgbibmhlephnhpmaofohgkpgkd
[+] Extension name:Phantom , CRX ID:bfnaelmomeimhlpmgjnjophhpkkoljpa
[+] Extension name:KHC , CRX ID:hcflpincpppdclinealmandijcmnkbgn
[+] Extension name:Temple - Tezos Wallet , CRX ID:ookjlbkiijinhpmnjffcofjonbfbgaoc
[+] Extension name:TezBox - Tezos Wallet , CRX ID:mnfifefkajgofkcjkemidiaecocnkjeh
[+] Extension name:DAppPlay , CRX ID:lodccjjbdhfakaekdiahmedfbieldgik
[+] Extension name:Polymesh Wallet , CRX ID:jojhfeoedkpkglbfimdfabpdfjaoolaf
[+] Extension name:ICONex , CRX ID:flpiciilemghbmfalicajoolhkkenfel
[+] Extension name:Yoroi , CRX ID:ffnbelfdoeiohenkjibnmadjiehjhajb
[+] Extension name:Station Wallet , CRX ID:aiifbnbfobpmeekipheeijimdpnlpgpp
[+] Extension name:Keplr , CRX ID:dmkamcknogkgcdfhhbddcghachkejeap
[+] Extension name:Byone , CRX ID:nlgbhdfgdhgbiamfdfmbikcdghidoadd
[+] Extension name:Coinbase Wallet extension , CRX ID:hnfanknocfeofbddgcijnmhnfnkdnaad
[+] Extension name:Guarda , CRX ID:hpglfhgfnhbgpjdenjgmdgoeiappafln
[+] Extension name:Trezor Password Manager , CRX ID:imloifkgjagghnncjkhggdhalmcnfklk
[+] Extension name:EOS Authenticator , CRX ID:oeljdldpnmdbchonielidgobddffflal
[+] Extension name:GAuth Authenticator , CRX ID:ilgcnhelpchnceeipipijaljkblbcobl
[+] Extension name:Nabox Wallet , CRX ID:nknhiehlklippafakaeklbeglecifhad

 

DynAPI Resolution

 

Lumma employs hash-based API resolution techniques, notably using the MurmurHash2 algorithm for hashing. This approach obscures APIs, requiring resolving functions with specific hash values and associated DLLs, adding layers of complexity to analysis. e,g.:0x5bd1e995:

 

LummaC2 Binary will pass two major calls to the API resolving function:

  • Hash of the desired API
  • .dll which contains the API

 

IDA xRef Resolution:

import idc
import idautils

API_FUNCTION = 0x471958 # Change to the relevant function call
APIS_FILE_PATH = '' # Output file for the strings

apiDict = {}

def getDLLRef(hash_addr):
    ref_addr = idc.prev_head(hash_addr)
    if idc.print_insn_mnem(ref_addr) == 'push':
        if idc.get_operand_type(ref_addr, 0) == idc.o_imm:
            dll_addr = idc.get_operand_value(ref_addr, 0)
            return idc.get_bytes(dll_addr, 50).split(b'\x00\x00')[0].replace(b'\x00',b'').decode()
    return None

def getHashDict(ref_addr):
    ref_addr = idc.prev_head(ref_addr)
    if idc.print_insn_mnem(ref_addr) == 'push':
        if idc.get_operand_type(ref_addr, 0) == idc.o_imm:
            hashVal = hex(idc.get_operand_value(ref_addr, 0))
            if hashVal not in apiDict or apiDict[hashVal] == None:
                dllVal = getDLLRef(ref_addr)
                apiDict[hashVal] = dllVal

for xref in idautils.XrefsTo(API_FUNCTION):
    getHashDict(xref.frm)

print(f'[+] {len(apiDict)} API hashes were extracted')

out = open(APIS_FILE_PATH, 'w')
for k, v in apiDict.items():
    out.write(f'{k} - {v}\n')
out.close()

[+] 18 API hashes were extracted

################
# OUTPUT FILE: #
################

0xe8ff1073 - crypt32.dll
0x864087d1 - crypt32.dll
0x7328f505 - kernel32.dll
0xc40f97d4 - advapi32.dll
0x507048c2 - winhttp.dll
0x406457c2 - winhttp.dll
0x7aa0edcc - winhttp.dll
0xb72f0de - winhttp.dll
0x59886bc0 - winhttp.dll
0x76b029a - winhttp.dll
0xf9f57cf0 - winhttp.dll
0xe268a0c1 - winhttp.dll
0xab3372e8 - winhttp.dll
0x5658bf2e - KernelBase.dll
0x23fef64a - advapi32.dll
0x5f086d32 - kernel32.dll
0xa2f80070 - kernel32.dll
0x2f9959e0 - kernel32.dll

Hash extraction from .dll (0x20) loaded from PEFile:

import pefile
from murmurhash2 import murmurhash2

DLLS_PATH = '/Users/igal/malwares/Lumma/29.03.2023/dlls/' # can be replaced with system32 folder
LUMMA_API_HASHES = '/Users/igal/malwares/Lumma/29.03.2023/LummaApiHashes.txt'

SEED = 0x20 # might be changed in upcoming builds

def hashDllAPI(dllName, apiHash):
    pe = pefile.PE(dllName)
    for export in pe.DIRECTORY_ENTRY_EXPORT.symbols:
        try:
            expName = export.name
            hashValue = murmurhash2(expName, SEED)
            if hex(hashValue) == apiHash:
                return expName.decode()
        except AttributeError:
            continue

apiHashesFile = open(LUMMA_API_HASHES,'r').read()
lines = apiHashesFile.split('\n')


for line in lines:
    args = line.split(' - ')
    dllName = hashDllAPI(f'{DLLS_PATH}{args[1]}', args[0])
    print(f'[+] {args[0]} - {dllName}')
[+] 0xe8ff1073 - CryptStringToBinaryA
[+] 0x864087d1 - CryptUnprotectData
[+] 0x7328f505 - ExpandEnvironmentStringsW
[+] 0xc40f97d4 - GetCurrentHwProfileA
[+] 0x507048c2 - WinHttpOpenRequest
[+] 0x406457c2 - WinHttpConnect
[+] 0x7aa0edcc - WinHttpCloseHandle
[+] 0xb72f0de - WinHttpSendRequest
[+] 0x59886bc0 - WinHttpWriteData
[+] 0x76b029a - WinHttpReceiveResponse
[+] 0xf9f57cf0 - WinHttpOpen
[+] 0xe268a0c1 - WinHttpSetTimeouts
[+] 0xab3372e8 - WinHttpAddRequestHeaders
[+] 0x5658bf2e - IsWow64Process2
[+] 0x23fef64a - GetUserNameA
[+] 0x5f086d32 - GetPhysicallyInstalledSystemMemory
[+] 0xa2f80070 - GetComputerNameA
[+] 0x2f9959e0 - GetSystemDefaultLocaleName

 

YARA Rule

 

rule Win_LummaC2 {
    meta:
        author = "dBouLabs"
        description = "LummaC2 Strings"
    strings:
		$obfuscatorString = "576xed" ascii wide
		$s1 = "dp.txt" ascii wide
		$s2 = "c2sock" ascii wide
		$s3 = "TeslaBrowser" ascii wide
		$s4 = "Software.txt" ascii wide
    condition:
        uint16(0) == 0x5a4d and all of ($s*) and #obfuscatorString > 10 and filesize < 1500KB
}

Conclusion

This exploration into Lumma Stealer’s tactics sheds light on evolving phishing methods, obfuscation techniques, and API resolution strategies. Further research and vigilance are essential in combating such sophisticated threats.
LummaC2 has adopted xLoader/Formbook’s ability for DynamicDNS. A Pseudo fast-flux style hosting that continuously changes host parameters to evade Spamhaus blacklist. This would suggest that the Lumma development team completes this setup, and in turn, hosts the C2 panel for the commercially sold user granting them a child panel for operating the malware.

 

    •  
Posted in Write-Ups