Overview
SHA256: 44c907b839c2afff1a9d6e6491e38de103de38b8028914fc4573705df812320c
Sample: https://bazaar.abuse.ch/sample/44c907b839c2afff1a9d6e6491e38de103de38b8028914fc4573705df812320c/
Lumma Stealer (aka LummaC2 Stealer) is an information stealer written in C language that has been available through a Malware-as-a-Service (MaaS) model on Russian-speaking forums since at least August 2022. It is believed to have been developed by the threat actor "Shamel", who goes by the alias "Lumma". Lumma Stealer primarily targets cryptocurrency wallets and two-factor authentication (2FA) browser extensions, before ultimately stealing sensitive information from the victim's machine. Once the targeted data is obtained, it is exfiltrated to a C2 server via HTTP POST requests using the user agent "TeslaBrowser/5.5"." The stealer also features a non-resident loader that is capable of delivering additional payloads via EXE, DLL, and PowerShell.
https://malpedia.caad.fkie.fraunhofer.de/details/win.lumma
Function Pointers
In the start function, it eventually invokes part of the data in
.text as codeWhen this is called, the data in this region at
loc_535EF1 is casted as a function and executedDeobfuscating payload
Within the casted function, a loop with a XOR operation on a memory region is executed 342023 times.
This could mean that the payload it’s XOR deobfuscating is 342023 bytes long
This eventually calls another function at memory region
edi , which is the deobfuscated payload earlierThe analysis from here onwards is trickier because any breakpoints we set on the casted function are ephemeral and will disappear if we restart the execution of the program
Stack Strings
The sample uses stack strings and dynamic loading of DLLs to avoid static detection
After loading the string, it then calls
mov eax, large fs:30h which loads the PEBA for loop here iterates over the libraries loaded
And within
loc_29412F0, it compares the string of each loaded library to kernel32.dll to locate it’s loaded memory regionAfter locating
kernel32.dll, it uses stack strings again to find the functions within kernel32.dllIn this instance, it’s searching for
GetProcAddressin
call near ptr unk_29301E0, it loads the address of the previously found kernel32.dllIt then traverses all the functions in
kernel32.dll until it matches GetProcAddressIf we look at the function definitions within
kernel32.dll, the first function sorted alphabetically is AcquireSRWLockExclusive, which is what we’re seeing hereComparing the strings to see if we have found
GetProcAddressOverall, it searches for these functions
GetProcAddress GlobalAlloc GlobalFree GetModuleHandleA LoadLibraryA VirtualAlloc VirtualFree VirtualProtect
Deobfuscation and Library Loading
There’s plenty of deobfuscation steps that take place after this to unpack the final payload.
The payload eventually loads other libraries and call their functions, such as loading
winhttp.dll and making an internet connection.Instead of stepping through the deobfuscation steps, we can step over them and get the deobfuscated data by doing a few things
- Configure IDA to pause on library loads
- Set a breakpoint on
WinHttpCrackUrl
- Configure IDA to NOT pause on library loads (so we don’t get interrupted)
- Let the program run until the breakpoint at
WinHttpCrackUrlis triggered
- Look at the Stack View to see the parameters being passed to
WinHttpCrackUrlto get the deobfuscated URL
In IDA, go to
Debugger -> Debugger Option and check Suspend on library load/unloadRun the program until it loads
winhttp.dllOn the right side of IDA which shows all loaded libraries, select
winhttp.dll which opens us functions defined in the librarySearch for the function
WinHttpCrackUrl. This takes in the full URI and separates it from the URL and the resource.Setting a breakpoint on the function entry
Now we execute the program, and after the malware deobfuscates the URL and calls
WinHttpCrackUrl we can see the URL value being passed into the functionWe can do this by looking at the
Stack ViewThe function definition of
WinHttpConnect is given as suchWINHTTPAPI BOOL WinHttpCrackUrl( [in] LPCWSTR pwszUrl, --> 0x0019F330 [in] DWORD dwUrlLength, --> 0x0019F334 [in] DWORD dwFlags, --> 0x0019F338 [in, out] LPURL_COMPONENTS lpUrlComponents --> 0x0019F33C );
To find out
pwszUrl, click on the stack data and Follow in hex dumpNow we get the deobfuscated URL
To get a more accurate representation of the string since it’s a wide string, change the encoding of the Text to
UTF-16Stepping through the program, we get the following URLs
https://t.me/fvTDOnvFcMdW https://xn--dta-gp6a/ https://RgadiantSoul.top/ https://shiningrstars.help/ https://mercharena.biz/ https://generalmills.pro/ https://stormlegue.com/ https://blast-hubs.com/ https://blastikcn.com/ https://nestlecompany.pro/ https://naturewsounds.help/ https://steamcommunity.com/profiles/76561199822375128/
Steam as a C2
The last C2 URL looks interesting as it’s a steam profile page
When we visit it, we see various C2 domains it has used as aliases to the account.
What’s possibly happening is that the malware queries this account page and extract out the username as the next stage C2.
Getting data from Steam C2
When the malware connects to the steam page, we set a breakpoint on
WinHttpReadData to see the data it downloads and extracts.We set another breakpoint on
WinHttpQueryDataAvailable which tells us if there is more data to download. We step through the downloading process until there is no more data left to downloadSet a breakpoint at the end of
WinHttpQueryDataAvailableStep until you exit
winhttp.dll and return to user code.Check the value of
EAX.If there is more data,
EAX will be non-zero, else it would be zeroExtracting username from Steam page
The malware deobfuscates the string
<span class="actual_persona_name">It’s likely going to search for this in the data downloaded from the steam profile page to get the C2 address
The URL
slnvypdex[.]cfy is extracted for further processingROT13 Deobfuscation
The URL
slnvypdex[.]cfy is ROT13 decoded to hacknestm[.]runThis is so the C2 URLs are not so obvious on the steam page
Connect to C2
It decodes the following strings to prepare a body to post to
hacknestm[.]run/NbdHAContent-Type: application/x-www-form-urlencoded act=life
And calls
WinHttpOpen and WinHttpConnect to make a connection to the siteUnfortunately, the C2 website is down. After calling
WinHttpConnect, the return value of EAX is 0
Returns a valid connection handle to the HTTP session if the connection is successful, or NULL otherwise.
https://learn.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpconnect#return-value
Spoofing connection
If the connection dies, the process ends and nothing is loaded.
To get around this, we can spoof a successful connection to a dummy C2
Configure the VM to point
hacknestm[.]run to google.comFlush the DNS
ipconfig /flushdns
Unfortunately this method does not work since its reaching out to a HTTPS connection, which means if you simply redirect the traffic like this, there will be an error in the certification
Changing the Stack
What we do instead is change the URL string that was pushed on the stack and passed as an argument to
WinHttpConnectIn the Stack view, click
Follow in disassemblyThis shows the string contents on the stack.
Click on the starting character
Edit -> Patch program -> Change ByteChange it to
google.com67 00 6F 00 6F 00 67 00 6C 00 65 00 2E 00 63 00 6F 00 6D 00
Remember to null terminate your strings!
Now when we continue the program, it makes a connection to
google.com, and EAX is filled with a proper internet handleEventually it makes a post request to the C2, in this case
google.comSpoofing an ok
After the malware makes a post request to the site, it downloads the return data.
Since we’re technically sending it to
https://google.com/NbdHA, we’re going to get a 404The malware deobfuscates the string
oksearches if ok is part of the return bodyTo pass the check, using the same technique above for modifying data, we change the first few bytes of the return data to
okDeobfuscating Payload
Once it receives an
ok from the dummy C2 server, it deobfuscates another region of shellcodeSecond POST
It then deobfuscates several strings and makes a
POST request to the same C2 earlier (which we have configured to google.com)act=receive_message&ver=4.0&lid=QkbdNl--Enterprise&j=
3rd Payload
We enter what seems to be a third payload that was deobfuscated earlier
There’s 53 cases to switch to based on the reply we got from the C2, which in this case it’s a
404 message from googleWhats likely happening here is the malware polling for actions from the hosted C2, and depending on the return data, it will perform possibly up to 53 different actions.
The 53 cases will be explored in the next post 😫