Blog
January 11, 2026

StealC/SmartLoader Analysis


Featured image for “StealC/SmartLoader Analysis”

Introduction

This analysis began as an attempt to reverse an obfuscated Lua script. What was initially thought to be Luraph obfuscated code unfolded into a multi-stage malware operation that leveraged a trojanized GitHub repository, LuaJIT, a custom VM obfuscation technique, staged payload deliveries via other GitHub accounts, and scheduled persistence.

The malware identified as StealC takes advantage of the Lua environment to conceal its core logic, drop files, dynamically reconstruct secondary components, and evade static inspection and casual sandboxing.

The goal of this article is to document how StealC operates and how its Lua-based execution enables stealth execution. This document consolidates a brief technical findings, reverse analysis, and behavioral analysis.

The StealC malware family does not appear to be new, with reports indicating that it was first observed in early 2023, written primarily in C. The presence of newer implementations leveraging Lua and LuaJIT suggests active development and an evolving threat model.

Technical Findings

Initial Access & Delivery

The initial access and delivery begin with a malicious GitHub repository called “crawlrec.” The malicious repository is a clone of a legitimate repository of the same name. The trojanized repository emphasizes that the unsuspecting victim should download the .zip file and launch the ‘launcher.cmd‘ file.

start luajit.exe lang.txt

The lang.txt file may look unsuspecting. However, upon reading the content, it becomes apparent that this is a heavily obfuscated malware written in Lua.

Execution Chain

  1. User executes launcher.cmd.
  2. lua.exe (malicious) runs obfuscated Lua (lang.txt).
  3. Environment checks and staging occur.
  4. Additional Lua scripts/modules are dropped (e.g., socket2.lua, encrypted socket.lua).
  5. Legitimate LuaJIT runtimes are dropped/used for persistence execution (ODMw.exe and ODi5.exe).
  6. Network activity begins (GitHub staging + C2).
  7. Scheduled task enforces daily execution.

When launcher.cmd is executed, StealC takes a screenshot of the desktop in the BMP format. The screenshot is then uploaded to the C2 server in a POST request.

A screenshot that the malware captures and sends back to the C2 server.

Malware Architecture

  • lua.exe – Actual malware (StealC infostealer)
    • UPX packed.
    • VM-aware and analysis-resistant.
    • Performs environment checks.
    • Collects data.
  • ODMw.exe / ODi5.exe – Legitimate LuaJIT runtimes
    • Stock LuaJIT 2.1.0-beta3 builds (MinGW/GCC).
    • No embedded C2 indicators.
    • Used as execution hosts for malicious Lua during persistence and fallback.
  • socket.lua and socket2.lua
    • Despite their names, it is currently unknown what these two Lua files contain. socket.lua appears to be encrypted while socket2.lua is using the same obfuscation technique as lang.txt.

Persistence

Once the system has been infected, StealC would start the persistence mechanism. The following was captured in ProcMon, in which ODMw.exe would execute once every 24 hours.

9:44:44.4003971 PM	luajit.exe	4060	Process Create	C:\Windows\SYSTEM32\schtasks.exe	SUCCESS	PID: 1420, Command line: schtasks /create /sc daily /st 14:57 /f /tn TaskScheduler_ODMw /tr ""C:\Users\vboxuser\AppData\Local\ODMw\ODMw.exe" "C:\Users\vboxuser\AppData\Local\ODMw\lang.txt""

Network Analysis

  • Consistent outbound traffic to 178.17.59.88 after execution.
  • A request to another GitHub repository is made https://github.com/bomcombo/html
  • POST requests of the BMP desktop screenshot and exfiltrated data.

Reverse Engineering Analysis

The lang.txt is a heavily obfuscated Lua file that may be using its own obfuscation technique. Attempts were made to deobfuscate the code entirely, but none were successful. However, there was some slight progress on the analysis.

Lua provides hooking mechanisms that allow runtime inspection and interception through its environment and metatable system. In this case, the malware appears to be aware of such techniques and implements safeguards that interfere with analysis before meaningful execution occurs.

Before attempting deobfuscation, the first step was to fingerprint the specific obfuscator used. Normally, an obfuscated Lua code will contain a string that identifies the specific obfuscator used. For example, a Luraph-obfuscated code would contain “LHP!” followed by a long encoded string. This malware provided no such strings.

At first glance, the lang.txt is minified, making the obfuscated code hard to read.

When formatted correctly, the true size of the malicious file increases from 347 KB to 1.29 MB.

The function P() is a reordering function for the table (array in Lua) “s” below it.

Provided is the breakdown example of what the P function is doing.

local reord = {4, 5, 1, 3, 2, {"th", "g", "in", "so", "me"}}
local G = reord[#reord] -- The "#" operator returns the length of a table (array portion only)
-- Since reord has 6 elements (indices 1-6), #reord evaluates to 6
-- Therefore: G = reord[6] = {"th", "g", "in", "so", "me"}
--[[ If we ran this in a for loop provided from the P function, the breakdown would look like this
Y = 1: reord[1] = 4 -> G[4] is "so".
Y = 2: reord[2] = 5 -> G[5] is "me".
Y = 3: reord[3] = 1 -> G[1] is "th".
Y = 4: reord[4] = 3 -> G[3] is "in".
Y = 5: reord[5] = 2 -> G[2] is "g".
Result: "something"]]

That is what the P function does and what the “s” array contains.

The second most interesting function is the G() function.

local function G(G)
    return s[G - 2893]
end

Although it’s extremely simple, this function returns deobfuscated strings from a specific index from which it was last called. It is called quite often.

Knowing that the functions Y(), P(), G(), and the “s” array were important for this malware to function properly, it was time to get dirty.

Hooking

As stated before, Lua provides hooking mechanisms that allow runtime inspection and interception. One of the most popular methods is metatable hooking. Hooking the metatable is reasonable since the malware contains “setmetatable” and “getmetatable.”

local old_setmetatable = setmetatable
setmetatable = function(tbl, mt)
    if mt and type(mt) == "table" then
        -- Neuter __gc and other metamethods that might be used for checks
        if mt.__gc then mt.__gc = function() end end
        if mt.__len then mt.__len = function() return 0 end end
    end
    return old_setmetatable(tbl, mt)
end

Overriding the built-ins, such as “error()” was also necessary:

local _error = error
error = function(msg, level)
    if type(msg) == "string" then
        local lower = msg:lower()
        if lower:find("tamper") or lower:find("detect") or 
            lower:find("integrity") or lower:find("checksum") or
            lower:find("modified") or lower:find("corrupt") then
            log("BLOCKED error() call: " .. msg)
            return nil
        end
    end
    return _error(msg, level)
end

When the malware is executed with the hooks in place, we are bombarded with “Tamper Detected!” messages non-stop, as shown in the desktop screenshot, until we forcefully terminate it.

Further analysis and testing revealed the source of the “Tamper Detected!” message.

D = G(3307)
P = s[D]
F = G(2971) -- Tamper detection 2971 - 2893 = 78 index array "Tamper Detected!"
D = P(F)
D = {}
P = s[G(3059)]

There were many attempts to bypass the detection system. No method seemed to have worked. Not even a deobfuscator such as Prometheus was able to successfully deobfuscate this malware.

String Dump

There was curiosity to see what the “s” array contained. Dumping the strings took little effort. At first, they are encoded in Base64.

If we don’t let the malware decode these strings, we won’t get readable results. However, if the malware decodes them, we start to see familiar strings.

Index 1072 provides information about a DLL path. There is no way to know if the malware is attempting to place or check for an existing DLL.

lua.exe Analysis

Despite its name, lua.exe is the actual malware responsible for data exfiltration. It comes packed with UPX, a popular open-source executable packer. Fortunately, UPX includes a built-in unpacking utility. Once decompressed, we could clearly see strings referencing the popular web browsers the malware targets, as well as Discord.

Less popular browsers such as 7Star, Chedot, and CentBrowser are also targeted.

Upon further analysis of the unpacked EXE, the StealC build path residual is displayed.

Behavioral Analysis

When the Lua variant of StealC is executed, it retrieves the system environment in which it is running.

Parent PID: 6248, Command line: "C:\Users\vboxuser\Desktop\crawlrec\crawlrec-v3.1\luajit.exe" .\lang.txt, Current directory: C:\Users\vboxuser\Desktop\crawlrec\crawlrec-v3.1\, Environment: 
;	=::=::\
;	ALLUSERSPROFILE=C:\ProgramData
;	APPDATA=C:\Users\vboxuser\AppData\Roaming
;	ChocolateyInstall=C:\ProgramData\chocolatey
;	ChocolateyLastPathUpdate=134105855910809638
;	CommonProgramFiles=C:\Program Files\Common Files
;	CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
;	CommonProgramW6432=C:\Program Files\Common Files
;	COMPUTERNAME=WINDOWSMACHINE
;	ComSpec=C:\Windows\system32\cmd.exe
;	DriverData=C:\Windows\System32\Drivers\DriverData
;	HOMEDRIVE=C:
;	HOMEPATH=\Users\vboxuser
;	LOCALAPPDATA=C:\Users\vboxuser\AppData\Local
;	LOGONSERVER=\\WINDOWSMACHINE
;	NUMBER_OF_PROCESSORS=8
;	OneDrive=C:\Users\vboxuser\OneDrive
;	OS=Windows_NT
;	Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\ProgramData\chocolatey\bin;C:\ProgramData\chocolatey\lib\lua51\tools;C:\Users\vboxuser\AppData\Local\Microsoft\WindowsApps;
;	PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL
;	PROCESSOR_ARCHITECTURE=AMD64
;	PROCESSOR_IDENTIFIER=AMD64 Family 23 Model 113 Stepping 0, AuthenticAMD
;	PROCESSOR_LEVEL=23
;	PROCESSOR_REVISION=7100
;	ProgramData=C:\ProgramData
;	ProgramFiles=C:\Program Files
;	ProgramFiles(x86)=C:\Program Files (x86)
;	ProgramW6432=C:\Program Files
;	PSModulePath=C:\Users\vboxuser\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
;	PUBLIC=C:\Users\Public
;	SESSIONNAME=Console
;	SystemDrive=C:
;	SystemRoot=C:\Windows
;	TEMP=C:\Users\vboxuser\AppData\Local\Temp
;	TMP=C:\Users\vboxuser\AppData\Local\Temp
;	USERDOMAIN=WINDOWSMACHINE
;	USERDOMAIN_ROAMINGPROFILE=WINDOWSMACHINE
;	USERNAME=vboxuser
;	USERPROFILE=C:\Users\vboxuser
;	windir=C:\Windows

The malware would then create two new directories named “ODMw” and “ODi5” along with a second lang.txt for ODMw, socket2.lua for ODi5, and lua51.dll for both directories. Two new executables, named after their respective directories, are dropped. Examining the string analysis for these executables reveals that they appear to be legitimate LuaJIT executables.

ODMw.exe and ODi5.exe carry these same LuaJit strings.

Static analysis yielded no suspicious strings or C2 IP addresses, casting doubt on the initial theory that these binaries served as beacons.

Network

After initial execution, a connection is made to the IP address 178.17.59.88. The screenshot that we saw earlier was sent in a POST request at /api/NTEsN2QsN2UsNTgsNWIsNjAsNjIsNjcsYyw3OSw=. Wireshark analysis shows that it’s sending “data” information and receiving “loader” information.

Network activity related to a GitHub request was detected, which led to a repository called “html” hosted by the user bomcombo. This repository contained staged obfuscated payloads in .txt formats. These additional payloads are dropped in hidden folders of AppData\Local\Microsoft\Windows\INETCache\IE\. These files are renamed with “[1].” As of this writing, this account has been either deactivated by GitHub or by the threat actors.

Persistence

Maintaining persistence in an infected system is crucial if the malware is designed to steal data. StealC does not overlook this. It is designed to execute daily at the same time that the user had first executed the malware.

9:44:44.4003971 PM	luajit.exe	4060	Process Create	C:\Windows\SYSTEM32\schtasks.exe	SUCCESS	PID: 1420, Command line: schtasks /create /sc daily /st 14:57 /f /tn TaskScheduler_ODMw /tr ""C:\Users\vboxuser\AppData\Local\ODMw\ODMw.exe" "C:\Users\vboxuser\AppData\Local\ODMw\lang.txt""

The malicious luajit.exe takes advantage of this, and ODMw.exe is assigned as a scheduled task to execute the secondary obfuscated lang.txt file.

Conclusion

The StealC/SmartLoader malware has evolved beyond its originally observed implementations, incorporating a Lua-based execution. While full deobfuscation of the payload was not feasible, this limitation appears to have been intentional, resisting both static and dynamic analysis.

The use of layered encoding and staged payload delivery is a cat-and-mouse approach that favors operational resilience. As a result, behavioral and execution analysis remained reliable methods for identifying and mitigating this threat.

Indicators of Compromise (IoC):

IoCs:

lang.txt (minified) - 60cbcfa732d06e5eab5d96ab9067376be20a50bfad7c29da78b444bfa20f641a

luajit.exe, ODMw.exe, ODi5.exe - 5343326fb0b4f79c32276f08ffcc36bd88cde23aa19962bd1e8d8b80f5d33953

socket.lua - 3d6d411da8494f988f014efc0bf2ad319da4223f76617f52cfa67fd419dd7764

socket2.lua - ac33f6a0bed938826ddc49f2efec0eb049e6f268dc93eeb09d97270af8572a51

lua.exe - 23aed48c13f11103429b3968acc7fafecfc19a44c93c5e5fc9bbbadbc23b15f3

lua51.dll - c7a657af5455812fb215a8888b7e3fd8fa1ba27672a3ed9021eb6004eff271ac

IP Address: 178.17.59.88

URL Endpoint: /api/NTEsN2QsN2UsNTgsNWIsNjAsNjIsNjcsYyw3OSw=

css.txt - 438b8a6fbc24a1e1e24bb0256723e5d10140c02524b65fa2ea7d14fd13f32d05

index.txt - 6062f46c16da64b735b16eb9fc427aeb209f0f16bef3003dcbfa9dd25287f78b

index_old.txt - 7410aa84002385941824a818d599abc4dae1dbece8c0740efc176e36d2d52bfc

ProcMon logs in .CSV format:

luajit.exe log: https://bin.disroot.org/?992302c263114f15#7Yc1xBPHGcUoqdmyxwekiqET6RAUoVyAh9Hw7EVvFz2x

lua.exe log: https://bin.disroot.org/?e4a657ca81d29cb7#GWyPxR9wEeTfrErUUf6o4DnMdEgmjVVy2fqqsnV2uYQ4