LummaStealer
🥷🏼

LummaStealer

Overview

SHA256: 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 code
notion image
notion image
notion image
When this is called, the data in this region at loc_535EF1 is casted as a function and executed
notion image

Deobfuscating 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
notion image
This eventually calls another function at memory region edi , which is the deobfuscated payload earlier
notion image
The 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
notion image
After loading the string, it then calls mov eax, large fs:30h which loads the PEB
notion image
A for loop here iterates over the libraries loaded
notion image
And within loc_29412F0, it compares the string of each loaded library to kernel32.dll to locate it’s loaded memory region
notion image
After locating kernel32.dll, it uses stack strings again to find the functions within kernel32.dll
In this instance, it’s searching for GetProcAddress
notion image
in call near ptr unk_29301E0, it loads the address of the previously found kernel32.dll
notion image
It then traverses all the functions in kernel32.dll until it matches GetProcAddress
notion image
If we look at the function definitions within kernel32.dll, the first function sorted alphabetically is AcquireSRWLockExclusive, which is what we’re seeing here
notion image
Comparing the strings to see if we have found GetProcAddress
notion image
Overall, 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
  1. Configure IDA to pause on library loads
  1. Set a breakpoint on WinHttpCrackUrl
  1. Configure IDA to NOT pause on library loads (so we don’t get interrupted)
  1. Let the program run until the breakpoint at WinHttpCrackUrlis triggered
  1. 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/unload
notion image
Run the program until it loads winhttp.dll
notion image
On the right side of IDA which shows all loaded libraries, select winhttp.dll which opens us functions defined in the library
notion image
Search for the function WinHttpCrackUrl. This takes in the full URI and separates it from the URL and the resource.
notion image
Setting a breakpoint on the function entry
notion image
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 function
We can do this by looking at the Stack View
notion image
The function definition of WinHttpConnect is given as such
WINHTTPAPI 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 dump
notion image
Now we get the deobfuscated URL
notion image
To get a more accurate representation of the string since it’s a wide string, change the encoding of the Text to UTF-16
notion image
Stepping 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.
notion image

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.
notion image
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 download
Set a breakpoint at the end of WinHttpQueryDataAvailable
notion image
Step until you exit winhttp.dll and return to user code.
Check the value of EAX.
notion image
If there is more data, EAX will be non-zero, else it would be zero
notion image

Extracting username from Steam page

The malware deobfuscates the string <span class="actual_persona_name">
notion image
It’s likely going to search for this in the data downloaded from the steam profile page to get the C2 address
notion image
The URL slnvypdex[.]cfy is extracted for further processing

ROT13 Deobfuscation

The URL slnvypdex[.]cfy is ROT13 decoded to hacknestm[.]run
This is so the C2 URLs are not so obvious on the steam page
notion image
notion image

Connect to C2

It decodes the following strings to prepare a body to post to hacknestm[.]run/NbdHA
notion image
Content-Type: application/x-www-form-urlencoded act=life
And calls WinHttpOpen and WinHttpConnect to make a connection to the site
notion image
notion image
Unfortunately, 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
notion image

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.com
notion image
Flush 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 WinHttpConnect
In the Stack view, click Follow in disassembly
notion image
This shows the string contents on the stack.
Click on the starting character
notion image
Edit -> Patch program -> Change Byte
notion image
Change it to google.com
notion image
67 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!
notion image
Now when we continue the program, it makes a connection to google.com, and EAX is filled with a proper internet handle
notion image
Eventually it makes a post request to the C2, in this case google.com
notion image

Spoofing 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 404
notion image
The malware deobfuscates the string oksearches if ok is part of the return body
notion image
To pass the check, using the same technique above for modifying data, we change the first few bytes of the return data to ok
notion image

Deobfuscating Payload

Once it receives an ok from the dummy C2 server, it deobfuscates another region of shellcode
notion image

Second POST

It then deobfuscates several strings and makes a POST request to the same C2 earlier (which we have configured to google.com)
notion image
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
notion image
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 google
notion image
notion image
Whats 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 😫