SmartLoader: Lua-based malware delivered through cloned GitHub repositories
Stage-by-stage analysis of a SmartLoader campaign: cloned GitHub repos, a renamed LuaJIT runtime, EtherHiding C2 on Polygon, and a stealer that bypasses Chrome App-Bound Encryption.
SmartLoader is a Malware-as-a-Service loader written in Lua. It is distributed through GitHub repositories that clone legitimate open-source projects and ship a malicious archive alongside the original source. The loader fingerprints the host, contacts hardcoded C2 servers, resolves a fallback C2 from a smart contract on the Polygon blockchain, and downloads a second-stage infostealer. This article describes each stage of the chain using artifacts from one active campaign, github.com/AJOBKA/teams-alive.
ThreatFeed tracked the wider campaign under the FakeGit name in July 2026: FakeGit Campaign Distributes Malware via 7,600 Malicious GitHub Repos. This post goes one level deeper into a single repository from that campaign.
Overview
| Attribute | Value |
|---|---|
| Malware family | SmartLoader (Lua-based MaaS loader) |
| Delivery | Cloned GitHub repository with binary payload committed into a subdirectory |
| Runtime | Renamed LuaJIT interpreter + Prometheus-obfuscated Lua script |
| C2 | Hardcoded IPs over HTTP/80, XOR-encoded; EtherHiding via Polygon RPC as fallback |
| Stage 2 | Infostealer with scheduled-task persistence and Chrome App-Bound Encryption bypass |
| Typical payload | Lumma Stealer or similar |

1. Delivery: cloned repository
The campaign uses repositories that copy a legitimate project in full – name, description, icons, source files, build scripts, license – and add a single archive in a subdirectory. The archive is linked through a raw URL (github.com/<account>/<repo>/raw/...), which redirects to raw.githubusercontent.com and bypasses the GitHub file viewer. The original source files are left unmodified, so a reviewer who inspects the code finds nothing wrong.
Example: AJOBKA/teams-alive is a clone of philippo-source/teams-alive, a Python mouse-jiggler that keeps Microsoft Teams status active.
| Attribute | Value |
|---|---|
| URL | https://github.com/AJOBKA/teams-alive |
| Account | AJOBKA, GitHub user ID 176820038 |
| Repository ID | 1184699127 |
| Commits | 9 |
| Stars / Forks / Watchers | 0 / 0 / 0 |
| Copied from original | teams_alive.py, build_windows.bat, build_macos.sh, installer.nsi, requirements.txt, MIT license, icons |
| Payload | childe/teams-alive-1.1.zip |
| Payload link | https://github.com/AJOBKA/teams-alive/raw/refs/heads/main/childe/teams-alive-1.1.zip |
Characteristics shared with other repositories in this campaign, as documented in Trend Micro’s March 2025 reporting on SmartLoader and Lumma Stealer:
- AI-generated README. Emoji-heavy section headers, generic “System Requirements” (2 GB RAM, 100 MB disk), and several download buttons all pointing to the same ZIP. The README refers to
teams-alive.exe; the archive containsunit.exe. The README author did not open the payload. - SEO-spam topics.
awesome-tools,cssharp,metamod,homeoffice,mousejiggler,remote-work– unrelated tags chosen for search indexing. - No community signals. Zero stars, forks, issues or watchers.
- Non-developer lures. Productivity utilities, game cheats, cracked software, AI tools. The target is an office user searching the web for a tool, not a developer reviewing code.
Timestamps inside the ZIP show when the archive was built, not when the repository was published or when a victim downloaded it. In the teams-alive case the archive contents predate the observed distribution by roughly six months.
2. Package structure
| File | Size | Role |
|---|---|---|
Application.cmd |
30 B | Launcher: start unit.exe package.txt |
unit.exe |
852 KB | Renamed LuaJIT interpreter (PE64, MSVC 2022) |
package.txt |
309 KB | Stage 1 loader – Lua script obfuscated with Prometheus |
None of the three files is malicious in isolation. The launcher is a one-line batch file. The interpreter is an unmodified LuaJIT build. The script is plain text. The malware exists only when the interpreter is executed with the script as its argument.
2.1 LuaJIT as a runtime
LuaJIT is a single ~800 KB executable with no dependencies. Its properties as a malware runtime:
- The interpreter has no malicious behaviour and no signature. Antivirus verdicts on it (VirusTotal 47/69 for the sample above) are driven by entropy heuristics: LuaJIT embeds a large block of compressed bytecode, which resembles a packer.
- Lua is not on common LOLBin lists and does not have detection coverage comparable to PowerShell, VBScript, JScript,
mshta.exe,rundll32.exeorcertutil.exe. - The malware logic is a text file. It passes mail gateways, DLP and antivirus, and can be changed without rebuilding a binary.
- LuaJIT’s Foreign Function Interface allows the script to call arbitrary Windows APIs directly (
CreateProcessA,WriteProcessMemory, COM). Process injection and persistence are implemented in the script without dropping additional executables.
The same “legitimate runtime plus script” pattern is used by PyInstaller-packaged Python malware, AutoIt malware and Node.js loaders.
2.2 Prometheus obfuscation
Prometheus is an open-source Lua obfuscator, originally written to protect Roblox scripts. Transformations present in package.txt:
| Technique | Appearance | Effect |
|---|---|---|
| Reverse constant folding | (-229328+229329)+G instead of 1+G |
No readable numeric constants |
| String encryption | "\210\049\250\175" decoded by helper functions at runtime |
No readable URLs, IPs or API names |
| Control-flow flattening | while L do if L<X then … elseif L<Y … |
State machine instead of linear code |
| Variable renaming | a,j,R,Z,o,F,X,S,W… |
No semantic identifiers |
| Opaque predicates | Always-true/false branches | Analysis noise |
The obfuscation hides but does not encrypt. The script must decode itself at runtime for the Lua VM to execute it, so an instrumented interpreter recovers the decoded strings – C2 IPs, RPC endpoints, contract address, XOR key, API names – without static reversal.
3. Stage 1: loader
Sequence executed by unit.exe package.txt:
- Geolocation check – HTTP request to
ip-api.com. Consistent with a CIS-region exclusion, standard in Russian-speaking MaaS ecosystems. - C2 beacon – HTTP/80 to hardcoded IPs (
217.119.129.99,194.48.248.94), XOR-encoded. Transmits hostname, username, OS version, public IP and geolocation. - Connectivity check – request to
www.microsoft.com. - EtherHiding lookup – read-only call to a Polygon smart contract through a public RPC endpoint to retrieve the current C2 address. The loader carries a list of RPC providers and iterates through them:
polygon.drpc.org,polygon.publicnode.com,polygon-mainnet.gateway.tatum.io,polygon-public.nodies.app. - Stage 2 download –
dist.luafromraw.githubusercontent.comto%TEMP%, then re-execution ofunit.exe dist.lua.
Two properties are relevant to defenders. First, the loader runs the full sequence within seconds of launch; there is no sleep or sandbox-evasion delay. Second, the hardcoded C2 IPs are contacted before the blockchain lookup, so EtherHiding serves as rotation and fallback, not as the primary channel.
3.1 EtherHiding
EtherHiding (named by Guardio Labs, 2023) is a dead-drop resolver on a public blockchain. The operator deploys a smart contract – here on Polygon, an Ethereum-compatible chain with low transaction cost – and stores the current C2 address in it. The malware performs an eth_call against the contract via any public RPC endpoint. Reads cost nothing and leave no on-chain record. When a C2 is blocked, the operator sends one transaction costing a few cents to update the contract; every infected host retrieves the new address on its next query.
Consequences:
- No takedown. The contract is permanent and no provider can remove it.
- RPC endpoints are shared infrastructure. They serve wallets, DeFi applications and exchanges. Blocking the category is viable for most corporate environments but not for organisations with legitimate blockchain use.
- Multiple providers. The loader falls back to the next RPC endpoint in its list within seconds. Blocking a single RPC domain has no material effect.
- Short IOC lifetime. C2 IPs read from the contract can be replaced at any time.
- The contract address is a stable identifier. It is embedded in the malware and cannot change without redistribution. Once extracted, defenders can perform the same
eth_callto monitor the operator’s infrastructure rotation over time.
3.2 XOR-encoded C2 traffic
C2 requests and exfiltrated data are XOR-encoded with a key embedded in the script and sent over plain HTTP. This is obfuscation, not encryption: with the key recovered from the deobfuscated script, captured traffic decodes with a trivial script. The choice of XOR over TLS reflects the constraints of dependency-free Lua rather than an oversight.
4. Stage 2: infostealer and persistence
dist.lua is the stealer stage. Behaviour from sandbox detonation (Windows 10):
4.1 Persistence
Three mechanisms are established:
| Mechanism | Detail |
|---|---|
Scheduled task CloudDrive_<random> |
%LOCALAPPDATA%\<random>\<random>.exe package.txt |
Scheduled task OfficeClickToRunTask_<random> |
%LOCALAPPDATA%\<random>\<random>.exe dist.lua |
Scheduled task Setup (/rl highest) |
C:\Windows\System32\oobe\Setup.exe |
The interpreter is copied to %LOCALAPPDATA%\<4-6 hex>\<same>.exe together with the scripts.
The third task uses a documented Windows behaviour. oobe\Setup.exe is a signed Microsoft binary from the Out-Of-Box Experience. If C:\Windows\Setup\Scripts\ErrorHandler.cmd exists, Setup.exe executes it – a hook originally intended for OEM deployment error handling. The stealer writes ErrorHandler.cmd containing start %LOCALAPPDATA%\<random>\<random>.exe dist.lua and schedules Setup.exe to run daily. The task list shows only a signed System32 binary with no unusual path or argument. Hunts for scheduled tasks pointing into user-writable directories do not surface it. MITRE T1546, Event Triggered Execution.
4.2 Chrome App-Bound Encryption bypass
Chrome historically protected cookies and saved passwords with DPAPI, which any process running as the same user can decrypt with a single API call. Chrome 127 (July 2024) introduced App-Bound Encryption: the DPAPI key is wrapped with a second key held by elevation_service.exe, a Chrome service running as SYSTEM, which releases it only to a caller whose on-disk path is a verified Chrome installation.
The stealer’s bypass:
- Drops a fake
Chromium.exeto%LOCALAPPDATA%\Programs\Chromium\Application\. - Launches the legitimate
chrome.exe. - Injects into it via
WriteProcessMemory. - From the injected context inside the real Chrome process, calls
elevation_serviceover COM. - The service validates the caller’s path, sees a genuine Chrome binary, and releases the key.
Resulting process chain: Chromium.exe → chrome.exe → elevation_service.exe. The same approach applies to Edge and Brave. An ABE bypass in a commodity stealer indicates that the MaaS ecosystem tracks browser hardening closely.
4.3 Collection and exfiltration
- Browser credentials and cookies from all Chromium-based browsers
- Cryptocurrency wallet files
- Installed software inventory and hardware fingerprint
Loot is staged in C:\ProgramData\<GUID>.txt and %USERPROFILE%\Documents\<32 hex>.json, XOR-encoded, and sent to the C2 over HTTP/80.
5. Mark-of-the-Web
Files downloaded from the Internet zone receive a Zone.Identifier alternate data stream:
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://github.com/AJOBKA/teams-alive
HostUrl=https://raw.githubusercontent.com/AJOBKA/teams-alive/...
When an archive is extracted with Explorer, the stream propagates to the extracted files. Application.cmd, package.txt and unit.exe all carry the referrer of the repository they came from. EDR products that log MotW writes therefore record the download origin independently of browser history, which is frequently cleared or absent. Operators who want to avoid this ship 7z archives (no MotW propagation) or ISO/VHD containers (MotW discarded). This campaign uses plain ZIP.
6. Defensive considerations
- Cloned repositories target non-developers. GitHub carries implicit trust and hosts the download on
raw.githubusercontent.com, a domain that is rarely restricted. Controls: awareness content on “tools from GitHub”; restrictions on executable archives fromraw.githubusercontent.com. - No single file is malicious. Signature and single-file analysis do not cover the modular design. Behavioural detection – interpreter from a user-writable path with a
.txt/.luaargument, non-browser process resolving blockchain RPC,schtasksreferencingoobe\Setup.exe, writes toSetup\Scripts\ErrorHandler.cmd, unsigned process spawningchrome.exeorelevation_service.exe– is the applicable layer. - Stage 1 completes in seconds. Response latency determines whether stage 2 lands. A detection that fires minutes after execution finds a persistent stealer with exfiltrated credentials.
- Block the RPC provider category. Individual RPC domains are one entry in a fallback list. Corporate endpoints have no business requirement to reach public blockchain RPC endpoints.
- DNS filtering does not see hardcoded C2. Beacons go direct-to-IP. Perimeter IP blocking and EDR network telemetry are the only controls that observe this traffic.
- Local admin rights and unmanaged VPN clients extend the impact of a stealer infection. A consumer VPN client bypasses corporate DNS filtering entirely.
7. Indicators of compromise
Block
| Type | Indicator |
|---|---|
| GitHub repository | https://github.com/AJOBKA/teams-alive |
| GitHub account | AJOBKA (user ID 176820038) – github.com/AJOBKA/*, raw.githubusercontent.com/AJOBKA/* |
| Payload URL | https://github.com/AJOBKA/teams-alive/raw/refs/heads/main/childe/teams-alive-1.1.zip |
| Payload URL (resolved) | https://raw.githubusercontent.com/AJOBKA/teams-alive/refs/heads/main/childe/teams-alive-1.1.zip |
| C2 IP | 217.119.129.99 TCP/80 (DE) |
| C2 IP | 194.48.248.94 TCP/80 (MD) |
| C2 IP | 217.119.129.97 TCP/80 (DE) |
| C2 IP | 144.31.57.123 TCP/80 (US) |
Monitor (shared infrastructure)
| Domain | Role |
|---|---|
ip-api.com |
Geolocation check |
polygon.drpc.org, polygon.publicnode.com, polygon-mainnet.gateway.tatum.io, polygon-public.nodies.app |
Blockchain RPC – EtherHiding |
raw.githubusercontent.com |
Stage 2 hosting |
SHA256
| Hash | File | Note |
|---|---|---|
b0f0b6e38f77c518ebfaf691d729636d82cc59dc2a329d7454e11f74a2cb2d3f |
Application.cmd |
Launcher (30 B) |
bbd438d3d7a59152f1dd5e45bb8d22ee1c07f95cfe42cebbe756aaf4feadc875 |
unit.exe |
LuaJIT runtime. MD5 d56c0fe75c39022348c381b524a8fa75 |
830ec7352972fd1eb24fcaf72349ef9a27dd9f26f24552d6b68b87ffeada1212 |
package.txt |
Stage 1 loader |
19bf54769e547cc09285a9aa7cbed46767d4bdb30eed31b2bab1ce76056f97a1 |
dist.lua |
Stage 2 |
b32dbfb39be970afc6178e096d606f01f32cfc8cb1e5afd4895835f5d15d31ea |
7d7752.exe |
Interpreter copy |
71244561ef74ee525126eca3a05257fef66320a46c2ad4b0f245c7cf4e98af6d |
ErrorHandler.cmd |
Persistence helper |
Host artifacts (random names could vary per infection)
%USERPROFILE%\Downloads\teams-alive-1.1.zip
<extract dir>\teams-alive-1.1\{Application.cmd, package.txt, unit.exe}
%LOCALAPPDATA%\Temp\dist.lua
%LOCALAPPDATA%\<4-6 hex>\<same>.exe + package.txt | dist.lua
%LOCALAPPDATA%\Programs\Chromium\Application\Chromium.exe
C:\Windows\Setup\Scripts\ErrorHandler.cmd
C:\ProgramData\<GUID>.txt
%USERPROFILE%\Documents\<32 hex>.json
Scheduled tasks: CloudDrive_*, OfficeClickToRunTask_*, Setup (oobe\Setup.exe /rl highest)
8. MITRE ATT&CK
| Tactic | Technique |
|---|---|
| Resource Development | T1588 Obtain Capabilities (cloned repository) |
| Initial Access | T1189 Drive-by Compromise (SEO-poisoned GitHub repository) |
| Execution | T1204.002 User Execution: Malicious File |
| Execution | T1059.011 Command and Scripting Interpreter: Lua |
| Execution | T1059.003 Windows Command Shell |
| Defense Evasion | T1027 Obfuscated Files (Prometheus) |
| Defense Evasion | T1036 Masquerading (cloned project, renamed interpreter) |
| Defense Evasion | T1036.005 Match Legitimate Name (fake Chromium.exe) |
| Discovery | T1016.001 Internet Connection Discovery |
| Command and Control | T1071.001 Application Layer Protocol: HTTP |
| Command and Control | T1102 Web Service (GitHub, blockchain RPC) |
| Persistence | T1053.005 Scheduled Task |
| Persistence | T1546 Event Triggered Execution (oobe\Setup.exe / ErrorHandler.cmd) |
| Credential Access | T1555.003 Credentials from Web Browsers |
| Credential Access | T1539 Steal Web Session Cookie |
| Collection | T1005 Data from Local System |
9. References
- Trend Micro – AI Assisted Fake GitHub Repositories Fuel SmartLoader and LummaStealer Distribution (March 2025)
- Netskope – Developers in the Crosshairs: Fake AI Tools Deliver Infostealer
- Intellibron – Lua-JIT SmartLoader: Analyzing the GitHub Campaign Delivering Stealer
- Guardio Labs – EtherHiding: Hiding Web2 Malicious Code in Web3 Smart Contracts (2023)
- Prometheus Lua Obfuscator –
github.com/prometheus-lua/Prometheus - Original project –
github.com/philippo-source/teams-alive