Lumma Stealer, also known as LummaC2 Stealer, is a customizable malware written in C/C++ that allows for efficient and low-level access to system resources. It uses extensive obfuscation and anti-analysis features, making it highly effective and hard to detect. It is distributed as a Malware-as-a-Service (MaaS) model, with several plans available on underground forums and Telegram channels. This malicious software regularly gets updates that improve and expand its functionality, making it a serious threat.

Lumma Stealer is designed to steal sensitive data from compromised devices. It targets private browser data, cryptocurrency wallets, email credentials, financial information, and more. To evade detection, it uses sophisticated techniques like encryption and event-controlled operations. Lumma Stealer specifically targets Windows operating systems from Windows 7 to Windows 11.

This blog post demonstrates how Wazuh can detect and respond to Lumma Stealer on a monitored Windows endpoint.

Behavioral analysis

  • Lumma Stealer employs an anti-sandbox technique (trigonometry) that monitors cursor movements to detect genuine human activity, delaying its execution until such behavior is observed. This approach allows it to effectively evade detection in many malware analysis environments that don’t simulate mouse actions realistically.
  • Once Lumma Stealer detects cursor movement, indicating human activity, it records the next five cursor positions by executing GetCursorPos(), with a 50-millisecond pause between each capture. This checks that the movements are human-like, ensuring it is not running in a sandboxed environment designed to mimic basic user activity.
  • Lumma Stealer evades detection by using event-controlled write operations, enabling it to write data only under specific conditions, which helps it mimic legitimate processes and avoid triggering alerts.
  • Lumma Stealer typically employs AES-256 encryption to obfuscate its payloads and exfiltrated data, concealing malicious activities from security tools and complicating reverse-engineering efforts. 
  • The malware detects the presence of a debugger by using functions like IsDebuggerPresent and OutputDebugString.
  • The malware creates a suspended BitLockerToGo.exe process and allocates memory using the VirtualAllocEx function. This allows Lumma Stealer to be executed within a trusted Windows process and evade detection through process masquerading.
  • The malware escalates privileges by locking its memory pages to stay in RAM, preventing writing to the disk. It then sets its token to SeDebugPrivilege, using a Microsoft debugging tool to elevate its process privileges.
  • The malware injects itself into random processes, consistently targeting svchost.exe, aspnet_wp.exe, and lsass.exe, to maintain stealth and manipulate registry keys and files without raising suspicion.
  • The malware uses the RtlUserThreadStart function to initialize the injected processes, load the required Windows DLLs, and send a DNS query to resolve the domain names of various hardcoded malicious addresses. Once the communication to the first domain is resolved, the information is encrypted using TLS v1.2.
  • In the final stage, the malware sends messages and receives obfuscated responses from the C2 server. It then begins collecting and exfiltrating sensitive data from the victim’s endpoint, encrypting it, and using custom obfuscation to conceal the stolen data during transmission.
  • The malware scans the compromised system for files with keywords like seed.txt, wallet.txt, and *.pdf to target and collect sensitive information such as private keys, wallet addresses, and login credentials.

Analyzed file

Below is the indicator of compromise (IoC) for the specific Lumma Stealer variant analyzed in this blog post.

Hash typeValue
MD5cf9a2518d062283a422f243273f7094f
SHA256b9c71471d52e93c38eeb069082dcdea935f928024b6db3fce153c63d4af1b27c

Infrastructure

We use the following infrastructure to demonstrate the detection of Lumma Stealer with Wazuh.

  • A pre-built, ready-to-use Wazuh OVA 4.9.1 Follow this guide to download the virtual machine.
  • A Windows 10 endpoint with Wazuh agent 4.9.1 installed. We call this the victim endpoint. Refer to the following installation guide to install the Wazuh agent. 

Wazuh detection

We use the following methods to detect the Lumma Stealer activities on a monitored Windows endpoint:

  • We use Sysmon to monitor system events and create rules on the Wazuh server to detect the behavior of Lumma Stealer on the victim endpoint.
  • Perform file signature scanning using VirusTotal integration with Wazuh.
  • Remove malicious files using the Wazuh Active Response module.

Configuration

Perform the following steps to configure Sysmon on the victim endpoint and forward logs in the Sysmon event channel to the Wazuh server for analysis.

Victim endpoint

1. Download Sysmon from the Microsoft Sysinternals page.

2. Extract the compressed Sysmon file to your preferred location. 

3. Download the sysmonconfig.xml file using PowerShell. Replace <SYSMON_EXECUTABLE_PATH> with the path to your Sysmon executable:

> wget -Uri https://wazuh.com/resources/blog/emulation-of-attack-techniques-and-detection-with-wazuh/sysmonconfig.xml -OutFile <SYSMON_EXECUTABLE_PATH>\sysmonconfig.xml

4. Using PowerShell with Administrator privileges, switch to the directory where the Sysmon executable is located. Then run the command below to install and start Sysmon:

> .\Sysmon64.exe -accepteula -i sysmonconfig.xml

5. Add the following configuration within the <ossec_config> block of the configuration file of the Wazuh agent located at C:\Program Files (x86)\ossec-agent\ossec.conf to forward Sysmon events to the Wazuh server:

<localfile>
  <location>Microsoft-Windows-Sysmon/Operational</location>
  <log_format>eventchannel</log_format>
</localfile>

6. Restart the Wazuh agent to apply the configuration changes:

> Restart-Service -Name wazuh

Wazuh server

In this section, we develop rules to detect Lumma Stealer behavior on the victim endpoint.

1. Create a new rule file for detecting Lumma Stealer:

# touch /var/ossec/etc/rules/LummaC2.xml

2. Add the rules below to the /var/ossec/etc/rules/LummaC2.xml file:

<group name="Lumma, LummaC2, malware,">

<!-- Lumma execution -->
<rule id="100501" level="12">
    <if_sid>61603</if_sid>
    <field name="win.eventdata.Image" type="pcre2">\.exe</field>
    <field name="win.eventdata.OriginalFileName" type="pcre2">GlobalCheats\.exe</field>
    <description>Suspicious process creation by Lumma Stealer.</description>
    <mitre>
        <id>T1204</id>
    </mitre>
</rule>

<!-- Credential dumping -->
<rule id="100502" level="12">
    <if_sid>61614</if_sid>
    <field name="win.eventdata.TargetObject" type="pcre2">HKLM\\System\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL</field>
    <field name="win.eventdata.Image" type="pcre2">BitLockerToGo\.exe</field>
    <description>Suspicious registry modification.</description>
    <mitre>
        <id>T1003</id>
    </mitre>
</rule>

<rule id="100503" level="12">
    <if_sid>61609</if_sid>
    <field name="win.eventdata.Image" type="pcre2">(?i)[c-z]:\\Windows\\BitLockerDiscoveryVolumeContents\\BitLockerToGo\.exe</field>
    <field name="win.eventdata.ImageLoaded" type="pcre2">(?i)[c-z]:\\Windows\\SysWOW64\\amsi\.dll</field>
    <description>Suspicious DLL loading by BitLockerToGo.exe.</description>
    <mitre>
        <id>T1059.001</id>
    </mitre>
</rule>

<!-- DLL process injection -->

<rule id="100504" level="12">
    <if_sid>61609</if_sid>
    <field name="win.eventdata.Image" type="pcre2">.*\.exe</field>
    <field name="win.eventdata.ImageLoaded" type="pcre2">(?i)[c-z]:\\Windows\\assembly\\NativeImages_v4\.0\.30319_64\\mscorlib\\.*\\mscorlib\.ni\.dll</field>
    <description>PowerShell process loaded mscorlib.ni.dll, possibly indicating process injection.</description>
    <mitre>
        <id>T1055</id> 
    </mitre>
</rule>

<rule id="100505" level="12">
    <if_sid>61609</if_sid>
    <field name="win.eventdata.Image" type="pcre2">.*\.exe</field>
    <field name="win.eventdata.ImageLoaded" type="pcre2">(?i)[c-z]:\\Windows\\Microsoft\.NET\\Framework64\\v4\.0\.30319\\clrjit\.dll</field>
    <description>PowerShell process loaded clrjit.dll, indicating potential process injection.</description>
    <mitre>
        <id>T1055.001</id> 
    </mitre>
</rule>

</group>

Where:

  • Rule ID 100501 is triggered when Lumma Stealer creates a suspicious process GlobalCheats.exe on the Victim endpoint.
  • Rule ID 100502 is triggered when Lumma Stealer creates a Registry key on the Victim endpoint.
  • Rule ID 100503 is triggered when Lumma Stealer terminates its original process GlobalCheats.exe, and starts another process, BitLockerToGo.exe, to modify the endpoint registry.
  • Rule ID 100504 is triggered when  Lumma Stealer injects mscorlib.ni.dll into C:\Windows\assembly\NativeImages_v4.0.30319_64\mscorlib.
  • Rule ID 100505 is triggered when Lumma Stealer injects clrjit.dll into C:\Windows\Microsoft.NET\Framework64\v4.0.30319.

3. Restart the Wazuh manager for the changes to take effect:

# systemctl restart wazuh-manager

Detection results

The screenshot below shows the alerts generated on the Wazuh dashboard when Lumma Stealer is executed on the Victim endpoint. Perform the following steps to view the alerts on the Wazuh dashboard.

1. Navigate to the Threat Hunting section.

2. Click on Explore agent and select the monitored endpoint.

3. Click + Add filter. Then, filter for rule.id in the Field section.

4. Select filter is one of in the Operator field.

5. Filter for 100501, 100502, 100503, 100504, and 100505 in the Values field.

6. Click Save.

Lumma Stealer dashboard.

VirusTotal integration

VirusTotal aggregates antivirus products and online scan engines, providing an API that can be queried with URLs, IP addresses, domains, or file hashes to identify security threats. You can configure Wazuh to automatically send requests to the VirusTotal API with the hashes of files created or modified on the victim endpoint.

For this integration, we configure the Wazuh File Integrity Monitoring module and VirusTotal to detect and scan files that are added or modified on specific directories on the Victim endpoint. Additionally, we configured the Wazuh Active Response module to remove the files identified as malicious by VirusTotal.

Victim endpoint

1. Add the following configuration within the <ossec_config> block in the C:\Program Files (x86)\ossec-agent\ossec.conf file to monitor for changes. In this blog post, we configure the FIM module to monitor the Downloads folder for all users.

<syscheck>
  <directories check_all="yes" realtime="yes">C:\Users\*\Downloads</directories>
</syscheck>

2. Restart the Wazuh agent to apply the changes by running the following PowerShell command as an administrator:

> Restart-Service -Name wazuh
Active response Python script configuration

We create an active response script to remove any known variant of Lumma Stealer immediately after VirusTotal identifies it as a threat.

1. Create an active response script remove-threat.py on the Victim endpoint with the following content:

#!/usr/bin/python3
# Copyright (C) 2015-2022, Wazuh Inc.
# All rights reserved.
 
import os
import sys
import json
import datetime
 
if os.name == 'nt':
    LOG_FILE = "C:\Program Files (x86)\ossec-agent\active-response\active-responses.log"
else:
    LOG_FILE = "/var/ossec/logs/active-responses.log"
 
ADD_COMMAND = 0
DELETE_COMMAND = 1
CONTINUE_COMMAND = 2
ABORT_COMMAND = 3
 
OS_SUCCESS = 0
OS_INVALID = -1
 
class message:
    def __init__(self):
        self.alert = ""
        self.command = 0
 
def write_debug_file(ar_name, msg):
    with open(LOG_FILE, mode="a") as log_file:
        log_file.write(str(datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')) + " " + ar_name + ": " + msg +"\n")
 
def setup_and_check_message(argv):
 
    # get alert from stdin
    input_str = ""
    for line in sys.stdin:
        input_str = line
        break
 
 
    try:
        data = json.loads(input_str)
    except ValueError:
        write_debug_file(argv[0], 'Decoding JSON has failed, invalid input format')
        message.command = OS_INVALID
        return message
 
    message.alert = data
 
    command = data.get("command")
 
    if command == "add":
        message.command = ADD_COMMAND
    elif command == "delete":
        message.command = DELETE_COMMAND
    else:
        message.command = OS_INVALID
        write_debug_file(argv[0], 'Not valid command: ' + command)
 
    return message
 
 
def send_keys_and_check_message(argv, keys):
 
    # build and send message with keys
    keys_msg = json.dumps({"version": 1,"origin":{"name": argv[0],"module":"active-response"},"command":"check_keys","parameters":{"keys":keys}})
 
    write_debug_file(argv[0], keys_msg)
 
    print(keys_msg)
    sys.stdout.flush()
 
    # read the response of previous message
    input_str = ""
    while True:
        line = sys.stdin.readline()
        if line:
            input_str = line
            break
 
    # write_debug_file(argv[0], input_str)
 
    try:
        data = json.loads(input_str)
    except ValueError:
        write_debug_file(argv[0], 'Decoding JSON has failed, invalid input format')
        return message
 
    action = data.get("command")
 
    if "continue" == action:
        ret = CONTINUE_COMMAND
    elif "abort" == action:
        ret = ABORT_COMMAND
    else:
        ret = OS_INVALID
        write_debug_file(argv[0], "Invalid value of 'command'")
 
    return ret
 
def main(argv):
 
    write_debug_file(argv[0], "Started")
 
    # validate json and get command
    msg = setup_and_check_message(argv)
 
    if msg.command < 0:
        sys.exit(OS_INVALID)
 
    if msg.command == ADD_COMMAND:
        alert = msg.alert["parameters"]["alert"]
        keys = [alert["rule"]["id"]]
        action = send_keys_and_check_message(argv, keys)
 
        # if necessary, abort execution
        if action != CONTINUE_COMMAND:
 
            if action == ABORT_COMMAND:
                write_debug_file(argv[0], "Aborted")
                sys.exit(OS_SUCCESS)
            else:
                write_debug_file(argv[0], "Invalid command")
                sys.exit(OS_INVALID)
 
        try:
            os.remove(msg.alert["parameters"]["alert"]["data"]["virustotal"]["source"]["file"])
            write_debug_file(argv[0], json.dumps(msg.alert) + " Successfully removed threat")
        except OSError as error:
            write_debug_file(argv[0], json.dumps(msg.alert) + "Error removing threat")
           
       
    else:
        write_debug_file(argv[0], "Invalid command")
 
    write_debug_file(argv[0], "Ended")
 
    sys.exit(OS_SUCCESS)
 
if __name__ == "__main__":
    main(sys.argv)

The os.remove() function in the active response Python script handles the removal of the malicious file:

os.remove(msg.alert["parameters"]["alert"]["data"]["virustotal"]["source"]["file"])

2. Download Python and run the installer. This works with Python 3.8.7 or later (with pip pre-installed). Then, select the following checkboxes during installation:

  • Add Python.exe to PATH.
  • Use admin privileges when installing py.exe.

Note: This step is optional if Python is installed on the victim endpoint.

3. Run the following command with administrative privilege to install Pyinstaller using PowerShell:

> pip install -U pyinstaller

4. Change to the directory where the Python script remove-threat.py is located and convert the file to an executable file with the following command:

> pyinstaller -F remove-threat.py

5. Move the executable file remove-threat.exe from the \dist folder under your current working directory to the C:\Program Files (x86)\ossec-agent\active-response\bin folder.

6. Restart the agent to apply the changes by running the following PowerShell command as an administrator:

> Restart-Service -Name wazuh

Wazuh server

1. Append the configuration below to the /var/ossec/etc/ossec.conf file to scan the files with VirusTotal:

<ossec_config>
  <integration>
    <name>virustotal</name>
    <api_key><API_KEY></api_key> <!-- Replace with your VirusTotal API key -->
    <rule_id>554,550</rule_id>
    <alert_format>json</alert_format>
  </integration>
</ossec_config>

Replace the <API_KEY> variable with your VirusTotal API key. The FIM rule IDs 554 and 550 detect file addition and modification events, respectively.

Active response configuration

1. Append the following configuration to the /var/ossec/etc/ossec.conf file:

<ossec_config>
  <command>
    <name>remove-threat</name>
    <executable>remove-threat.exe</executable>
    <timeout_allowed>no</timeout_allowed>
  </command>

  <active-response>
    <disabled>no</disabled>
    <command>remove-threat</command>
    <location>local</location>
    <rules_id>87105</rules_id>
  </active-response>
</ossec_config>

2. Add the following rules to the /var/ossec/etc/rules/LummaC2.xml file to generate alerts when the Wazuh Active Response module successfully removes the malicious files.

<group name="virustotal,">
  <rule id="100601" level="12">
    <if_sid>657</if_sid>
    <match>Successfully removed threat</match>
    <description>$(parameters.program) removed threat located at $(parameters.alert.data.virustotal.source.file)</description>
  </rule>

  <rule id="100602" level="12">
    <if_sid>657</if_sid>
    <match>Error removing threat</match>
    <description>Error removing threat located at $(parameters.alert.data.virustotal.source.file)</description>
  </rule>
</group>

Where:

  • Rule ID 100601 generates an alert when the Wazuh Active Response module successfully removes the Lumma Stealer.
  • Rule ID 100602 generates an alert when the Wazuh Active Response module fails to remove the Lumma Stealer.

3. Restart the Wazuh manager to apply configuration changes:

# sudo systemctl restart wazuh-manager

Visualize results

When a variant of  Lumma Stealer is downloaded to the victim’s Downloads folder, Wazuh generates alerts and promptly initiates an active response to remove the malicious file. The screenshot below shows that the Wazuh FIM module detects the file addition, which VirusTotal confirms as malicious, triggering Wazuh to take an automated response. Follow these steps to view these alerts:

1. Navigate to the Threat Hunting section.

2. Click on Explore agent and select the monitored endpoint.

3. Click + Add filter. Then, filter for rule.id in the Field section.

4. Select filter is one of in the Operator field.

5. Filter for 100601, 553, 550, and 87105 in the Values field.

6. Click Save.

Lumma Stealer results

You can further expand the active response alert to inspect the alert’s details.

Wazuh dashboard details

Conclusion

Lumma Stealer is a malware designed to exfiltrate sensitive data, posing a significant risk to infected systems. Detecting and responding to such threats requires a comprehensive security platform. Wazuh, a free and open source SIEM/XDR platform, provides the visibility and detection capabilities necessary to safeguard your endpoints from such emerging threats.

In this blog post, we demonstrated how integrating Sysmon with Wazuh can effectively detect the behavior of Lumma Stealer. Additionally, we utilized the Wazuh FIM and VirusTotal integration to identify the malware upon its download to an endpoint. We leveraged the Wazuh Active Response module to show how malicious files can be swiftly removed, neutralizing the threat before it causes damage.

To learn more about Wazuh capabilities, check out our documentation, blog posts, and community for support and updates.

Reference