smartloader: same luajit binary in 17 repos across 14 cves
Out of 2,712 PoC repository archives scanned by rdintel, 76 scored as malicious. 17 of those repos share the same two file hashes, spread across 15 GitHub accounts and 14 unrelated CVEs. All 17 scored 1.0 (maximum malware confidence).
The binaries are luajit.exe (99 KB) and lua51.dll (3,449 KB). The payload is SmartLoader, which delivers StealC via process hollowing.
what rdintel flagged
Our scanner fires on seven distinct signals for these repos. Here is the output from KvzinNcpx7/kvzinncpx7.github.io:
[renamed_interpreter] lua51.dll is a renamed LuaJIT binary (3449 KB)
[interpreter_with_payload] lua51.dll (LuaJIT) loads payload: arch.txt (347 KB)
[interpreter_with_payload] luajit.exe (LuaJIT) loads payload: arch.txt (347 KB)
[nested_archive_with_exe] io_github_kvzinncpx_v2.3.zip contains executables: lua51.dll, luajit.exe
[nested_archive_with_scripts] io_github_kvzinncpx_v2.3.zip contains scripts: Launcher.cmd
[bat_launches_exe] Launcher.cmd launches: luajit.exe
[obfuscated_payload] arch.txt (347 KB) contains obfuscated code:
lua varargs pattern, single-line packed code, high special-char ratio
The same pattern repeats across all 17 repos: a ZIP containing luajit.exe + lua51.dll + an obfuscated .txt file + a .cmd launcher. The exe hashes are identical. The bytecode payload names change per repo.
the archive contents
| file | sha256 | size |
|---|---|---|
luajit.exe |
5343326fb0b4f79c32276f08ffcc36bd88cde23aa19962bd1e8d8b80f5d33953 |
99 KB |
lua51.dll |
c7a657af5455812fb215a8888b7e3fd8fa1ba27672a3ed9021eb6004eff271ac |
3,449 KB |
| bytecode payload | varies per repo | 335-347 KB |
Launcher.cmd |
627b500fc6ae782368f39fc36e7ac5f3da38b962070a07f71782526e548dac03 |
<1 KB |
luajit.exe scores 31/76 on VirusTotal. BitDefender detects it as Gen:Heur.FakeGit.1. lua51.dll scores 0/76 because it is a legitimate Lua 5.1 runtime. The bytecode file (arch.txt, ico.txt, clib.txt, etc.) looks like random text to static analysis.
all 17 repositories
The ZIP name, bytecode filename, and repo description vary per repo, but the executables are identical.
| # | account | repository | cve | zip name | payload file |
|---|---|---|---|---|---|
| 1 | KvzinNcpx7 | kvzinncpx7.github.io | CVE-2025-9074 | io_github_kvzinncpx_v2.3.zip | arch.txt |
| 2 | freiwi | CVE-2025-8110 | CVE-2025-8110 | CV-2.0.zip | arch.txt |
| 3 | kikiuuw | kikiuuw.github.io | CVE-2025-68921 | github_io_kikiuuw_1.0-alpha.1.zip | ico.txt |
| 4 | kikiuuw | CVE-2025-68921 | CVE-2025-68921 | CV_3.6.zip | ico.txt |
| 5 | juccoblak | CVE-2025-6554 | CVE-2025-6554 | CVE-2025-6554_1.6.zip | clib.txt |
| 6 | siddu7575 | CVE-2025-61882-CVE-2025-61884 | CVE-2025-61882 | CVE-2025-61882-CVE-2025-61884.zip | vm.txt |
| 7 | yogeshkumar09 | yogeshkumar09.github.io | CVE-2025-55184 | yogeshkumar-io-github-v2.9.zip | arch.txt |
| 8 | yogeshkumar09 | CVE-2025-55184_Testing | CVE-2025-55184 | CV-Testing-v3.9.zip | arch.txt |
| 9 | Black-and-reds | reactguard | CVE-2025-55183 | Software-3.8.zip | cdef.txt |
| 10 | Asder10 | asder10.github.io | CVE-2025-55182 | github_io_asder_v2.0.zip | license.txt |
| 11 | vick333-peniel | vick333-peniel.github.io | CVE-2025-55182 | peniel-io-github-vick-v3.5.zip | cdef.txt |
| 12 | sakyu7 | sakyu7.github.io | CVE-2025-43529 | io-github-sakyu-1.9.zip | clx.txt |
| 13 | Nikopmpm | nikopmpm.github.io | CVE-2024-0670 | github_nikopmpm_io_v1.9-alpha.4.zip | arch.txt |
| 14 | Hitplus | hitplus.github.io | CVE-2023-39910 | github_io_hitplus_1.2.zip | arch.txt |
| 15 | Boydunbarred375 | gi-cv | CVE-2020-14144 | gi-cv-3.4.zip | license.txt |
| 16 | Riocipta75 | lab-cve-2020-0610 | CVE-2020-0610 | lab-cve-2020-0610.zip | - |
| 17 | mzuhair9933 | PoPE-pytorch | CVE-2016-0856 | pytorch-P-Po-v3.5-alpha.2.zip | icon.txt |
First seen: 2022-01-12. Last seen: 2026-01-26.
repo descriptions are ai-generated
Every repo has an AI-generated description and README. Examples from our scan data:
reactguard: “Detect vulnerabilities in React Server Components with ReactGuard”PoPE-pytorch: “Implement polar coordinate positional embedding in PyTorch for efficient attention”gi-cv: “Create a documentation-as-code CV showcasing your skills and experience”CVE-2025-9074: “Exploit CVE-2025-9074 using this API exploitation framework designed for Docker environments”CVE-2025-6554: “Demonstrate and validate the addressof and fakeobj primitives in the V8 sandbox”
All descriptions start with an emoji. Three repos (reactguard, PoPE-pytorch, gi-cv) disguise themselves as legitimate projects. The rest name-drop the CVE they are impersonating.
Several READMEs link directly to the malicious archive hosted in the repo or in a related account’s repo. Our scanner flagged these as doc_links_to_archive.
overlapping accounts across clusters
rdintel tracks 18 malware clusters total. Three accounts from this cluster also appear in separate clusters with different binaries:
| account | this cluster | also in |
|---|---|---|
| sakyu7 | luajit.exe (CVE-2025-43529) | reinit.exe cluster (sha256:5b6f8ee0..., 4 repos, 4 CVEs) |
| mzuhair9933 | luajit.exe (CVE-2016-0856) | reinit.exe cluster |
| Asder10 | luajit.exe (CVE-2025-55182) | unc.exe cluster (sha256:30694a01..., 2 repos, 2 CVEs) |
These accounts distribute multiple unrelated malware binaries. This confirms they are not compromised accounts with a single injected repo; they are operated as part of the campaign.
delivery via github pages
8 of 17 repos are GitHub Pages sites (*.github.io). This means the malware is served both to people who clone the repo and to anyone who visits the page URL. The Pages sites use the repo description as page content, creating a landing page that links to the archive download.
execution chain
Launcher.cmd runs start luajit.exe conf.txt (or arch.txt, ico.txt, etc. depending on the repo). The console window is hidden immediately via GetConsoleWindow + ShowWindow(SW_HIDE).
luajit.exe loads lua51.dll and interprets the bytecode. The Lua payload declares an 8,514-byte FFI C definition block and calls Windows APIs directly:
Process injection:
CreateProcessA(CREATE_SUSPENDED) -> NtUnmapViewOfSection -> VirtualAllocEx
-> WriteProcessMemory -> SetThreadContext -> ResumeThread
Targets: openwith.exe, dialer.exe, dllhost.exe, rundll32.exe.
Alternative path: VirtualAlloc(PAGE_EXECUTE_READWRITE) + CreateThread.
Reconnaissance: GetComputerNameW, IsWow64Process, GetSystemMetrics, screenshot via GDI chain (GetDC / CreateCompatibleDC / CreateDIBSection / BitBlt), then http://ip-api.com/json/ for geolocation. Exfiltrated with static user-agent s3klqc1wa6ntg1szvckzi0nt9oumpwy6hhq.
FFI structures: IMAGE_DOS_HEADER, IMAGE_NT_HEADERS32/64, IMAGE_EXPORT_DIRECTORY, TOKEN_ELEVATION, OSVERSIONINFOEXW, PEB/LDR (manual API resolution to avoid import table analysis).
C2 resolution
SmartLoader resolves C2 by calling a Solidity getter on Polygon Mainnet:
| contract | function | selector | deployed |
|---|---|---|---|
0xd68910ED4D4A5A9bAdF9ec95604CAE0f3378479B |
getDomain() |
0xb68d1809 |
2025-02-21 |
0x2cbd2464dd749f5c0034fc9cddc6db2d53dea400 |
getDomain() |
0xb68d1809 |
- |
0x1823A9a0Ec8e0C25dD957D0841e3D41a4474bAdc |
getData() |
0x3bc5de30 |
2025-11-15 |
RPC: polygon-rpc.com. The operator called destroyContract() on the V1 contract on 2026-01-26; it failed because EIP-6780 (post-Dencun) prevents selfdestruct from clearing contract state.
Resolved domains: layer1.icu (registered 2025-02-21), aproxy.app (registered 2025-03-10).
Fallback: hex-encoded blobs fetched from GitHub dead drop repos (github.com/rd898/package, github.com/Mahmudul-Riad/www, github.com/aidagluglu/files, github.com/AKboss1221/fortigate-belsen-leak). Decrypted with 32-byte XOR key ECe6VGLRJum2qYtl79OiOU7aHot7Zhbn.
obfuscation
Lua bytecode uses the Prometheus obfuscator. ESET tracks 16 distinct generations (Lua/Agent.Z through .BT).
- Custom Base64 alphabet:
L07ker/2Jn4Z+b6y8hfXYVpTiDCvQKc3qRjUEBdo9MzHxtGNgWuA1FSmlaPO5wIs - P table: 1,078 entries of encrypted constants (~114KB per payload)
- Three-pass shuffle on P table:
{1,1064},{1,658},{659,1064} - State machine dispatcher over encrypted bytecode
- All strings assembled from P table at runtime; zero plaintext
- Addition cipher:
(plaintext + key) mod 256, key89pCO1NlLRkTZgb8DtZmKwC42AQcUeXF
PE crypter and stealc
The crypter loads AES-256-ECB-encrypted payload from PE resource RCDATA (ID 101). Two hardcoded 32-byte constants XORed via AVX2 (vpxor ymm1, ymm0, [rbp+0x20]) produce the AES key. Base64url decode precedes decryption. Output: 779,808 bytes.
AES keys: YOYd5_dZpfW_nmA-rTxg61gpdbu_26-u (build0, 2025-02-27), 51AabzlG90_c_K9JZ8YXRGTOFralTd8k (build6, 2025-03-02).
StealC (TrojanPSW:Win64/StealC) resolves 120+ Win32 APIs via PEB walking. Config encrypted with RC4 (keys: qAw6EF2z3ycMeACsJA, dVhllnj7MBHRDVTFDT).
Targets: Chromium Login Data/Cookies/Web Data/History/IndexedDB/App-Bound Encryption, Firefox logins.json/cookies.sqlite via NSS3 (PK11SDR_Decrypt), Outlook (Office 13.0-16.0), Steam (ssfn*, config.vdf), WinSCP sessions, FoxMail.
persistence
%AppData%\ODE3\ODE3.exe (renamed luajit.exe)
%AppData%\ODE3\module.class (bytecode)
%AppData%\ODE3\lua51.dll (runtime)
%USERPROFILE%\Pictures\<MachineGUID> (task cache)
%TEMP%\session.lua / debug.lua (secondary payloads)
Scheduled tasks:
SecurityHealthService_ODE3
ApplicationExperienceAnalyzeris_ODE4
WindowsErrorReporting_<Base64EncodedLoaderID>
Registry: HKCU:\SOFTWARE\SibCode\
C2 IPs
89.169.13.215 95.164.53.26 150.241.108.62
77.105.164.178 89.169.12.179 87.120.36.50
178.236.243.5 80.66.81.11 150.241.105.82
213.176.73.80 91.196.34.40 89.169.12.173
89.169.12.241 217.119.129.110 213.176.72.200
detection
Test-Path "$env:APPDATA\ODE3"
Get-ScheduledTask | Where-Object {
$_.TaskName -match 'SecurityHealthService_ODE|ApplicationExperienceAnalyzeris_ODE|WindowsErrorReporting_ODE'
}
Get-ItemProperty "HKCU:\SOFTWARE\SibCode\" -ErrorAction SilentlyContinue
Get-ChildItem "$env:USERPROFILE\Pictures" -Filter "*-*-*-*-*" -Directory
AV: BitDefender Gen:Heur.FakeGit.1, ESET Lua/Agent.Z through .BT, Microsoft TrojanPSW:Win64/StealC.
If executed: rotate browser passwords, session cookies, crypto wallet keys, Outlook credentials, Steam sessions, WinSCP saved sessions, any API keys on the machine.
mitre att&ck
| id | technique |
|---|---|
| T1566.002 | Spearphishing Link |
| T1204.002 | User Execution: Malicious File |
| T1059.007 | Scripting Interpreter (Lua) |
| T1036.005 | Masquerading |
| T1027 | Obfuscated Files |
| T1140 | Deobfuscate/Decode |
| T1102.001 | Dead Drop Resolver |
| T1055.012 | Process Hollowing |
| T1113 | Screen Capture |
| T1555.003 | Credentials from Browsers |
| T1539 | Steal Web Session Cookie |
additional hashes
# stealc
ca42898e885979196647b6e5c469461a25870c1f15ef5910d531f37f8f3f147f build0
63c8cedc49339dd788dbc25f851dfce5219045aef80dd9bd17343948fb73f954 build6
# related luajit variants
1cf20b8449ea84c684822a5e8ab3672213072db8267061537d1ce4ec2c30c42a lua.exe
ff976f6e965e3793e278fa9bf5e80b9b226a0b3932b9da764bffc8e41e6cdb60 lua51.dll variant
# bytecode payloads
4b1ba932f06251267820b1af5f0a533f869e809bb9c6a1729a8097553d2f874e conf.txt
57df281639cfd3245818757cd30887b1128153b8d3d2e013166a362acd0ec72a import.ui
# archives
ebb0c76a03b8bb0ba246b8b31143f4462b4c3b0b3b5d581c499b4c3a484fd792 Application.zip
81580758604dff8b2b8f9126645e4a897e9b86b63bede420a07b1a6b3a973638 Application.zip variant
# related clusters (shared accounts)
5b6f8ee0072386b4b63cbcb8f83ef010005d4b6ed3cbd906a094a69726475d62 reinit.exe
30694a0101abfeea642cb9de7fb7eb66789eea74d8d7257b39822d7dab59445d unc.exe
references
- FakeGit: LuaJIT malware distributed via GitHub at scale - derp.ca
- Distribution of SmartLoader Malware via Github Repository - ASEC/AhnLab
- JIT Happens: Exposing LuaJIT Malware - Security Blue Team
- SmartLoader: Analyzing the GitHub Campaign - Intellibron
- AI Assisted Fake GitHub Repositories - Trend Micro
- SmartLoader: Large-scale infiltration via GitHub - Gatewatcher
- Joe Sandbox Report #706907 - luajit.exe
- IOCs for SmartLoader to Lumma Stealer - Unit42
- SmartLoader Wireshark dissector - Security Blue Team