AgentBaiting / FakeGit — SmartLoader to StealC via Fake AI Skills and MCP Servers
Executive Summary
Attacker objective: harvest live browser sessions, credentials, cloud tokens and developer secrets from workstations by getting a human or an AI coding agent to download and run a Windows ZIP that presents itself as an AI Skill or an MCP server.
Island published research on 2026-07-20 describing a wave of the long-running FakeGit operation: about 7,600 malicious GitHub repositories created by roughly 6,600 profiles, of which more than 800 pose as AI Skills or Model Context Protocol (MCP) servers. Those AI-capability repositories were reproduced more than 600 times across public registries including LobeHub, Glama, MCP.so and MCP Market, and the wider operation recorded more than 14 million measured downloads across GitHub Release assets in around 200 repositories. The AI-themed wave built through March and peaked in April 2026.
The new part is the delivery route, not the malware. Island calls it AgentBaiting: the repositories are written to be discovered and parsed by AI agents. An agent searching for a capability finds the repository on its own, treats the attacker's README as legitimate documentation, and hands the install instructions to the operator. In Island's testing Claude Code, Gemini and ChatGPT all surfaced campaign repositories without ever being given a link. This turns a discovery step that used to require a human judgment call into an automated recommendation.
What actually runs: the ZIP contains no MCP manifest and no server code. It contains three or four files: a one-line .cmd or .bat launcher, a renamed LuaJIT interpreter, an optional lua51.dll, and a 300 KB single-line obfuscated Lua script disguised with a benign extension such as .txt, .log, .ico, .class or .ui. The launcher runs start <exe> <payload>. The interpreter is a GUI-subsystem build so no console appears, and the Lua stage additionally calls GetConsoleWindow plus ShowWindow(SW_HIDE) through the LuaJIT FFI. SmartLoader then fingerprints the host, screenshots the desktop, resolves its live C2 from a value stored in a Polygon smart contract, pulls encrypted second stages from GitHub, and reflectively loads a packed StealC PE.
Highest-value defensive angle: the execution chain is structurally weird and does not resemble any legitimate software install. A cmd.exe child that is an unsigned interpreter in a user-writable path, taking a .txt-extension file as its only argument, is a near-unique signature. Two masquerading daily scheduled tasks under %LOCALAPPDATA% named <BenignProductName>_<Base64LoaderId> give a second, independent catch point. Both are cheap to hunt and hard for the operator to change without rewriting the loader.
Defender priority: StealC takes live sessions and OAuth grants, not just passwords. If you confirm SmartLoader execution, password resets alone do not contain it. Revoke browser sessions, OAuth grants, API tokens, cloud keys and developer credentials for the affected user, and treat every AI Skill / MCP server installed from an unvetted repository in the last 120 days as suspect.
Source Review & Web Hunter Notes
Thirteen sources fetched and snapshotted on 2026-07-28 into AgentBaiting-SmartLoader-Hunt-sources/. Every atomic indicator in this pack traces to at least one of those snapshots.
| Tier | Source | Key finding | Carry forward |
|---|---|---|---|
| 1 · Primary | Island — AgentBaiting: How 800+ Fake AI Skills and MCP Servers Delivered Malware (2026-07-20) | Campaign scale, AgentBaiting technique, ZIP internals (application.cmd / luau.exe / ico64.txt), 10 repository + ZIP SHA-256 pairs | Yes |
| 1 · Primary | Hexastrike — Cloned, Loaded, and Stolen: 109 Fake GitHub Repositories Delivered SmartLoader and StealC | Full reverse-engineering of the same loader family: Polygon dead-drop resolver, contract address and selector, bare-IP C2, two-task persistence, staged StealC PE, loader/payload hashes | Yes |
| 1 · Primary | Centri — JIT Happens: Exposing LuaJIT Malware In The Wild | Independent SmartLoader dynamic analysis: ip-api.com geolocation check, task caching in the Pictures folder, %APPDATA%\<Base64LoaderId>\ staging, session.lua/debug.lua in %TEMP%, static Lua user agent, scheduled task WindowsErrorReporting_ODEw | Yes |
| 1 · Primary | AhnLab ASEC — Distribution of SmartLoader via GitHub Repository Disguised as a Legitimate Project | Four-file ZIP layout (java.exe / Launcher.cmd / lua51.dll / module.class), persistence copy to %AppData%\ODE3, tasks SecurityHealthService_ODE3 and WindowsErrorRecovery_ODE4, C2 task JSON schema | Yes |
| 2 · Corroborating | Intellibron — Lua-JIT SmartLoader: Analyzing the GitHub Campaign Delivering Stealer | Second confirmation of SecurityHealthService_ODE3 plus ApplicationExperienceAnalysis_ODE4; compiler.exe / import.ui / Launch.bat variant; ip-api.com lookup | Yes |
| 2 · Corroborating | Cloud Security Alliance Labs — research note on FakeGit / AgentBaiting and the MCP supply chain | Ecosystem context: registry vetting failures, share of published Skills carrying critical issues, scale of the MCP registry | Partial (framing only) |
| 2 · Trade press | BleepingComputer, The Hacker News, Help Net Security, DevOps.com, TechNadu, CyberSecurityNews, GBHackers | Independent restatement of scale and technique; CyberSecurityNews independently reproduces three of Island's ZIP SHA-256 values; The Hacker News adds the historical link to the Lumma-era operation tracked as Water Kurita | Partial |
Analyst decisions
- Two waves, one loader. Island documents the July 2026 AI-Skills/MCP wave; Hexastrike, ASEC, Centri and Intellibron document the same SmartLoader family from earlier waves. The lure surface changed. The post-execution chain did not. Indicators are labelled by wave in section 4 so nobody mistakes an April C2 address for current infrastructure.
- Nothing ships at prevent/critical. Only the three ZIP hashes that appear in two independent sources are proposed at
prevent, and at severityhigh. Everything single-sourced ships atdetectorhunt. - The Polygon-resolved C2 is deliberately perishable. The operator rotates it by updating one on-chain value. The two observed bare-IP addresses are last-seen 2026-04-12 and are shipped as historical
detect, with the behavioural resolver hunt (Q5) carrying the durable detection weight. - No repository allow/deny list ships. With ~7,600 repositories across ~6,600 accounts and active rotation, a name list is stale on arrival. The pack hunts the execution chain and the install path instead.
- Attacker-authored text encountered in a legitimate source. Island quotes the campaign README's own install instructions as evidence ("download the .exe from the Releases page, click More info then Run anyway"). That is reproduced research evidence describing the lure, not guidance to act on, and it did not influence any value in this pack.
Hunt Brief & Attack Chain
Attack chain
| # | Step | Telemetry | Hunt angle |
|---|---|---|---|
| 1 | Attacker clones or fabricates a project, publishes it under a lookalike profile (one character off a real developer), writes a convincing README, attaches a Windows ZIP | None on-endpoint | External: repository inventory, publisher verification |
| 2 | Repository is indexed by public MCP / Skill registries, reproducing the attacker README and download link | Proxy / web gateway | Egress logs to registry domains followed by a GitHub release download |
| 3 | AgentBaiting: a human or an AI coding agent searching for a capability discovers the repository and surfaces the install instructions | Agent CLI logs, shell history, ProcessRollup2 parentage | Q10 — coding-agent process spawning a shell that downloads or extracts an archive |
| 4 | ZIP downloaded and extracted to Downloads / Temp / Desktop | ZipFileWritten, PeFileWritten | Q9 — AI/MCP-themed archive dropped; Q2 — Lua runtime unpacked to a user-writable path |
| 5 | User runs the launcher: a one-line .cmd/.bat containing start <exe> <payload> | ProcessRollup2 | Q1 — the core signature: cmd.exe parent, unsigned interpreter child in a user path, data-file argument |
| 6 | Renamed LuaJIT interpreter (GUI subsystem, unsigned, no version metadata) executes the Prometheus-obfuscated Lua payload; console hidden via FFI ShowWindow | ProcessRollup2, PeFileWritten | Q1, Q2; IOA on the parent/child/extension triple |
| 7 | Anti-debug check using native shellcode copied into executable memory | Memory-scan detections only | Gap — no reliable process-level telemetry |
| 8 | Host geolocation and fingerprinting via ip-api.com; desktop screenshot captured through GDI | DnsRequest, NetworkConnectIP4 | Q6 — geo-IP lookup from a non-browser process |
| 9 | C2 resolution: JSON-RPC eth_call to a Polygon RPC endpoint against a fixed contract address and function selector; contract returns the live C2 URL | DnsRequest, proxy POST bodies | Q5 — blockchain RPC from a non-browser process; proxy inspection for the selector |
| 10 | Encrypted second stages pulled from an attacker-controlled GitHub repository (raw content), decrypted in memory | DnsRequest, proxy | Q7 — raw GitHub content fetched by a non-developer process |
| 11 | Persistence: two daily scheduled tasks masquerading as audio, Office, Security Health or Windows Error Reporting components, staged under %LOCALAPPDATA% / %APPDATA% in a Base64-named directory | ScheduledTaskRegistered, ProcessRollup2 | Q3 — task naming pattern; Q4 — schtasks.exe pointing into AppData |
| 12 | Host data and screenshot exfiltrated to a bare-IP C2 as multipart/form-data; encrypted tasking returned as JSON | NetworkConnectIP4, proxy | Q8 — known C2 addresses; proxy rule on bare-IP POST |
| 13 | Packed StealC PE decrypted from a staged blob and reflectively loaded; browser credentials, cookies, live sessions, extension data, mail and remote-access credentials, screenshots exfiltrated | Falcon behavioural / memory detections | Partial — rely on StealC signatures and credential-access IOAs |
Hunt hypotheses (ordered by fidelity)
| # | Hypothesis | MITRE | Falcon events | Expected FP | Conf |
|---|---|---|---|---|---|
| H1 | A batch launcher started an unsigned interpreter from a user-writable directory, passing a non-script data file as its only argument | T1204.002, T1059.003, T1036.005 | ProcessRollup2, SyntheticProcessRollup2 | Rare. Some game mod loaders and a few legacy installers | High |
| H2 | A Lua runtime component (lua51.dll or a LuaJIT interpreter) was written into Downloads, Temp, AppData or ProgramData | T1105, T1036.005 | PeFileWritten, NewExecutableWritten | Game clients, Wireshark, Redis modules, some CAD tools | High |
| H3 | A scheduled task was registered whose name is a benign Windows/Office product name followed by an underscore and a short Base64-looking token | T1053.005, T1036.004 | ScheduledTaskRegistered | Very rare. Some vendor updaters append a GUID, not a 3-8 char token | High |
| H4 | schtasks.exe /create registered an action pointing at a binary under AppData or ProgramData with a text-extension argument | T1053.005 | ProcessRollup2 | Software deployment agents, Teams/Chrome/Edge updaters | Medium |
| H5 | A non-browser, non-wallet process resolved a public blockchain RPC endpoint (dead-drop resolver) | T1102.001, T1568 | DnsRequest, SuspiciousDnsRequest | Crypto wallets, web3 dev tooling, node.exe in blockchain repos | Medium |
| H6 | A non-browser process performed a geolocation lookup against a public IP-info service shortly after first execution | T1614, T1082 | DnsRequest | VPN clients, some telemetry SDKs, licensing checks | Medium |
| H7 | Raw GitHub content was fetched by a process that is not a browser, package manager or developer tool | T1102, T1105 | DnsRequest | High in engineering estates. Scoped by process, not by domain | Medium |
| H8 | An endpoint connected to a documented SmartLoader or StealC bare-IP C2 | T1071.001, T1041 | NetworkConnectIP4 | Effectively none, but the addresses rotate | Medium |
| H9 | An archive whose name is themed around AI, MCP, Skills or an agent was written to Downloads, Temp or Desktop | T1195.001, T1608.001 | ZipFileWritten | High in AI-forward estates. Triage aid, not an alert | Low |
| H10 | An AI coding agent process spawned a shell that downloaded, cloned or expanded an archive from a code-hosting site | T1195.001, T1105, T1059 | ProcessRollup2 | High wherever agents are sanctioned. Baseline first | Low |
Gap flagged from the scaffold: steps 7 (native anti-debug shellcode) and 13 (in-memory StealC load) have no reliable process-level telemetry. They are covered only by Falcon behavioural and memory-scan detections, and are marked GAP in the coverage map.
Consolidated IOC Table
Read the wave column. The July 2026 AI-Skills wave supplies the ZIP hashes. The April 2026 wave supplies the loader, payload and network infrastructure, which is the same malware family but older infrastructure. Nothing here is a substitute for the behavioural queries in section 8.
Delivery packages — AI Skills / MCP wave (July 2026)
| Type | Value | Context | Conf | Action |
|---|---|---|---|---|
| sha256 | 91e5dbfaf45edf25fbc2168f92083e05dfa427afa7633e991392e33cc7427dad | awesome-skills-claude-3.3.zip — repo spoofing a widely used Claude Skills collection | High | prevent |
| sha256 | c15693106682f2ddb26649cab6e1962a64537627cde4c5d3c79d5a0be8c1b5a8 | mcp-walmart-2.2.zip — the repo Gemini and ChatGPT both recommended in testing | High | prevent |
| sha256 | 66afc7d87d10dbe392898c4e5c613e0442fabb396415c2bef3a5ef2ac752c5ad | server_databricks_mcp_1.6.zip — three-file ZIP: launcher, renamed LuaJIT runtime, disguised Lua payload | High | prevent |
| sha256 | 216a2c99fd42c00f9323d8b16dd19f622f7f4778b2b1d7cf07a3de5621fd1546 | yu-ai-agent-1.0-beta.3.zip | High | detect |
| sha256 | 498fe8fb806cd0e6685f97fc7d74de769dae5a28cdc821557b7585ad5ad83147 | agent_ai_awesome_skills_2.0.zip | High | detect |
| sha256 | 62744baa8077bb8be237647fd78e3bea2ca0932bf4be3d5618600f97118095f8 | for-security-mcp-3.3.zip — lure aimed at security teams | High | detect |
| sha256 | 1da8df487d30b988f3c350c065206726aaa13f079a07151cd42ab5579994b9de | plugins_claude_awesome_code_2.4.zip | High | detect |
| sha256 | a33f40cab1ab7f971d3464af3e7595918107332b9e83342007571842b9e22826 | mcp-server-jenkins-3.2.zip — build-infrastructure lure | High | detect |
| sha256 | 3c858facbad66f5479e2c4add171421dc1b6488b36f33e7cff073aba585954a7 | gateway-docker-mcp-v1.6-alpha.5.zip | High | detect |
| sha256 | fc1278f419e611bf40ca414099bfd9ad98a31ffb054371e8cb65a84849b00eaf | alibabacloud-skills-bigdata-v1.7.zip | High | detect |
Loader components and payloads — April 2026 wave
| Type | Value | Context | Conf | Action |
|---|---|---|---|---|
| sha256 | fad3d429172932b72e50f52af169a80439464e3538d97810509090e2e6cdf32a | SmartLoader LuaJIT interpreter shipped as loader.exe | High | detect |
| sha256 | bff0904456e3151221d29ed1d7c88fc31587efbdfb28817cdcb7ec7f20cade21 | SmartLoader LuaJIT interpreter shipped as selector.exe | High | detect |
| sha256 | bbd438d3d7a59152f1dd5e45bb8d22ee1c07f95cfe42cebbe756aaf4feadc875 | SmartLoader LuaJIT interpreter shipped as unit.exe | High | detect |
| sha256 | 3595a6b226ce4daa0a28edea152b3a887c01f6323db1d082f6568c995cdefb55 | SmartLoader LuaJIT interpreter shipped as luajit.exe | High | detect |
| sha256 | 09e0f7616dfd2f7eb2876f6ef7331d6dbc78775acd594a94b0397a56717d1fcc | SmartLoader-bundled lua51.dll runtime | High | detect |
| sha256 | 440ceb0dc5911faca54ed9a4dd186dad3d006ae4f52d0bb7d1e4b4edd8c3693a | SmartLoader-bundled lua51.dll runtime | High | detect |
| sha256 | 87de3e5a8ef669589c421220cd392ae8027a8f8d3cd97d35ac339f87dcff12c8 | Packed x64 StealC PE recovered from the staged GitHub blob; PE timestamp precedes staging-repo creation by minutes | High | detect |
Network and infrastructure
| Type | Value | Context | Conf | Action |
|---|---|---|---|---|
| ipv4 | 144.31.57.65 | SmartLoader C2 returned by the Polygon contract. Last seen 2026-04-12. Rotates. | Medium | detect |
| ipv4 | 144.31.57.67 | SmartLoader C2, same hosting range as the above | Medium | detect |
| ipv4 | 213.176.73.149 | StealC C2 | Medium | detect |
| contract | 0x1823A9a0Ec8e0C25dD957D0841e3D41a4474bAdc | Polygon smart contract used as the dead-drop resolver. Durable across C2 rotation, so higher value than the IPs above. | High | hunt |
| selector | 0x3bc5de30 | Contract function selector called to retrieve the C2 string. Hunt in proxy POST bodies. | High | hunt |
| domain | polygon.drpc.org | Public Polygon RPC endpoint abused for the eth_call. Legitimate service. Alert on the calling process, never on the domain alone. | Medium | hunt |
| url | https://raw.githubusercontent.com/deepanshugoel99/long/refs/heads/main/long/long/message1.txt | Encrypted second-stage Lua blob | High | hunt |
| url | https://raw.githubusercontent.com/deepanshugoel99/long/refs/heads/main/long/long/message2.txt | Encrypted StealC PE blob | High | hunt |
| uri-pattern | POST /api/<base64_victim_id> | Exfiltration endpoint, multipart/form-data with host metadata and screenshot | High | hunt |
| uri-pattern | POST /task/<base64_victim_id> | Task-completion callback, returns HTTP 204 | High | hunt |
| user-agent | s3klqc1wa6ntg1szvckzi0nt9oumpwy6hhq | Static Lua user agent observed across requests in an independent SmartLoader analysis | Medium | hunt |
Host artefacts (behavioural — these are the durable ones)
| Type | Value | Context | Conf | Action |
|---|---|---|---|---|
| task-name | SecurityHealthService_ODE3 | Persistence task. Confirmed independently by two analyses. | High | hunt |
| task-name | WindowsErrorRecovery_ODE4 · ApplicationExperienceAnalysis_ODE4 · WindowsErrorReporting_ODEw · AudioManager_ODM3 · OfficeClickToRunTask_7d7757 | Same masquerade family across samples. The stable part is the shape: benign product name, underscore, 3-8 char Base64-looking loader ID. | High | hunt |
| directory | %LOCALAPPDATA%\<Base64LoaderId>\ and %APPDATA%\<Base64LoaderId>\ | Staging directory holding the copied interpreter, lua51.dll and the cached Lua stage | High | hunt |
| filename | lua51.dll · luajit.exe · luau.exe · java.exe · loader.exe · unit.exe · selector.exe · boot.exe · compiler.exe · load.exe · util.exe | Renamed LuaJIT interpreters and the Lua runtime DLL. Unsigned, no version metadata, GUI subsystem. | Medium | hunt |
| filename | Launcher.cmd · Launcher.bat · Application.cmd · Application.bat · App.bat · Launch.bat · ErrorHandler.cmd · application.cmd | One-line launchers containing start <exe> <payload> | Medium | hunt |
| filename | module.class · import.ui · ico64.txt · conf.txt · proto.txt · package.txt · buff.log · session.lua · debug.lua | Prometheus-obfuscated single-line Lua payloads, roughly 296-309 KB, disguised with benign extensions | Medium | hunt |
Affected Surface & Telemetry Matrix
| Surface | Why it is exposed | Required telemetry | Priority | Gap risk |
|---|---|---|---|---|
| Windows developer and analyst workstations | Primary execution target. The ZIP is a Windows package and the whole chain is Win32. | Falcon sensor: ProcessRollup2, PeFileWritten, ScheduledTaskRegistered, DnsRequest, NetworkConnectIP4 | Critical | Low — standard sensor coverage |
| AI coding agents and their host processes (CLI agents, IDE extensions, MCP clients) | AgentBaiting makes the agent the discovery and recommendation layer. Agent-initiated downloads inherit the user's session. | ProcessRollup2 parentage, agent CLI transcripts, shell history | Critical | High — agent transcripts are rarely centralised, and parentage alone does not show intent |
| MCP server / Skill configuration directories | A malicious capability is installed by writing config, not by installing a package. There is no package manager to audit. | File-integrity monitoring on agent config paths; source control on the config repo | High | High — Falcon does not emit file-write telemetry for arbitrary JSON/YAML by default |
| Browser session and token stores | StealC's actual objective. Cookies, live sessions, extension data. | Falcon credential-access IOAs; IdP sign-in logs for impossible-travel after infection | Critical | Medium — theft itself is quiet; detect via downstream session reuse |
| Web proxy / egress | Blockchain RPC, raw GitHub content and bare-IP POST all traverse it. | Proxy logs with URL path, method and user agent; TLS inspection for the eth_call body | High | Medium — without TLS inspection the contract selector is invisible |
| Software inventory / AI capability register | You cannot match newly published campaign repositories against your estate without knowing what is installed. | Inventory of Skill/MCP source repo, commit, version, package hash | High | High — almost nobody has this yet |
| macOS / Linux developer machines | Observed packages are Windows-only, but the lure and the registry listings are cross-platform. | Process telemetry for the same discovery-to-download path | Low | Low — no non-Windows payload documented in the cited research |
ATT&CK Mapping
| Tactic | Technique | Observed behaviour | Query or control |
|---|---|---|---|
| Resource Development | T1608.001 — Stage Capabilities: Upload Malware | ~7,600 repositories host malicious ZIPs in Releases or in the tree | External; hardening H-S1 (curated catalog) |
| Resource Development | T1585.001 — Establish Accounts: Social Media / Developer Accounts | ~6,600 profiles, several a single character off a real developer's handle | Hardening H-I3 (verify publisher, not project) |
| Initial Access | T1195.001 — Supply Chain Compromise: Software Dependencies and Development Tools | Poisoned AI Skill / MCP capability supply chain, amplified by registry listings | Q9, Q10; hardening H-I1 |
| Execution | T1204.002 — User Execution: Malicious File | User or agent-guided operator runs the extracted launcher | Q1 |
| Execution | T1059.003 — Command and Scripting Interpreter: Windows Command Shell | One-line .cmd/.bat containing start <exe> <payload> | Q1, Q4 |
| Execution | T1059 — Command and Scripting Interpreter (Lua via LuaJIT) | Prometheus-obfuscated Lua executed by a renamed LuaJIT interpreter using FFI to reach Win32 directly | Q1, Q2; IOA-1 |
| Persistence | T1053.005 — Scheduled Task/Job: Scheduled Task | Two daily tasks with independent recovery paths (cached local stage, and re-download from GitHub) | Q3, Q4 |
| Defense Evasion | T1036.005 — Masquerading: Match Legitimate Name or Location | LuaJIT renamed to java.exe, luau.exe, compiler.exe; payload given a .txt/.class/.ui extension | Q1, Q2 |
| Defense Evasion | T1036.004 — Masquerade Task or Service | Task names imitating Security Health, Windows Error Reporting, Office Click-to-Run, audio managers | Q3 |
| Defense Evasion | T1564.003 — Hide Artifacts: Hidden Window | GUI-subsystem interpreter plus GetConsoleWindow and ShowWindow(SW_HIDE) via FFI | Behavioural only — no direct query |
| Defense Evasion | T1027 / T1140 — Obfuscated Files, Deobfuscate/Decode | Single-line ~300 KB Prometheus-obfuscated Lua; encrypted GitHub-staged blobs decrypted in memory | File-size heuristic in native hunts |
| Defense Evasion | T1622 — Debugger Evasion | Anti-debug check via native shellcode copied into executable memory | GAP |
| Defense Evasion | T1620 — Reflective Code Loading | PE parsing structures plus thread-creation primitives used to load StealC without touching disk | GAP — memory-scan detections only |
| Discovery | T1082 / T1614 — System Information and Location Discovery | Host fingerprint JSON; geolocation lookup against a public IP-info service | Q6 |
| Collection | T1113 — Screen Capture | Desktop screenshot captured through the GDI pipeline and uploaded with the fingerprint | Covered indirectly via Q8 exfil traffic |
| Command and Control | T1102.001 — Web Service: Dead Drop Resolver | JSON-RPC eth_call to a Polygon RPC endpoint against a fixed contract and selector returning the live C2 | Q5 |
| Command and Control | T1102 / T1105 — Web Service, Ingress Tool Transfer | Encrypted second-stage Lua and StealC PE pulled from raw GitHub content | Q7 |
| Command and Control | T1071.001 — Application Layer Protocol: Web Protocols | HTTP POST to a bare-IP C2 with multipart/form-data | Q8 |
| Credential Access | T1555.003 — Credentials from Web Browsers | StealC harvests browser passwords, mail and remote-access credentials | Falcon credential-access IOAs |
| Credential Access | T1539 — Steal Web Session Cookie | Live sessions and extension data taken, which is why password resets alone do not contain it | IdP session revocation (containment phase 2) |
| Exfiltration | T1041 — Exfiltration Over C2 Channel | Screenshot and fingerprint uploaded to /api/<base64_victim_id> | Q8; proxy rule |
Native Audit-Log Hunts (non-CQL)
Run these where Falcon telemetry is thin: agent configuration files, scheduled-task inventory, and the AI capability register.
N1 — Enumerate scheduled tasks matching the SmartLoader masquerade shape
Any task whose name is a benign product name followed by an underscore and a short alphanumeric token, whose action lives under a user-writable directory.
Get-ScheduledTask | ForEach-Object {
$a = ($_.Actions | Where-Object { $_.Execute })
foreach ($act in $a) {
if ($act.Execute -match '(?i)\\(AppData|ProgramData)\\' -and
$_.TaskName -match '^[A-Za-z]+_[A-Za-z0-9]{3,8}$') {
[pscustomobject]@{
Task = $_.TaskName
Path = $_.TaskPath
Exec = $act.Execute
Args = $act.Arguments
Author = $_.Author
}
}
}
} | Format-Table -AutoSize
N2 — Find staged Lua runtime components under user profiles
$roots = @($env:LOCALAPPDATA, $env:APPDATA, "$env:USERPROFILE\Downloads", $env:TEMP)
Get-ChildItem -Path $roots -Recurse -Force -ErrorAction SilentlyContinue `
-Include 'lua51.dll','luajit.exe','luau.exe' |
Select-Object FullName, Length, CreationTime,
@{n='Signed';e={ (Get-AuthenticodeSignature $_.FullName).Status }} |
Format-Table -AutoSize
N3 — Find single-line Lua payloads disguised with benign extensions
The payloads are 296-309 KB and are one single line. Legitimate .txt, .log and .ico files of that size are essentially never one line.
$roots = @($env:LOCALAPPDATA, $env:APPDATA, "$env:USERPROFILE\Downloads", $env:TEMP)
Get-ChildItem -Path $roots -Recurse -Force -File -ErrorAction SilentlyContinue `
-Include '*.txt','*.log','*.ico','*.license','*.class','*.ui','*.dat' |
Where-Object { $_.Length -gt 200KB -and $_.Length -lt 600KB } |
ForEach-Object {
$lines = 0
try { $lines = (Get-Content -LiteralPath $_.FullName -TotalCount 3 -ErrorAction Stop).Count } catch {}
if ($lines -le 1) { [pscustomobject]@{ File=$_.FullName; KB=[int]($_.Length/1KB); Created=$_.CreationTime } }
} | Format-Table -AutoSize
N4 — Inventory installed MCP servers and Agent Skills
There is no package manager here, so the config files are the inventory. Diff this output against a reviewed catalog and treat anything unrecognised as unvetted until proven otherwise.
$paths = @( "$env:USERPROFILE\.claude", "$env:USERPROFILE\.cursor", "$env:USERPROFILE\.codex", "$env:APPDATA\Claude", "$env:APPDATA\Code\User" ) Get-ChildItem -Path $paths -Recurse -Force -File -ErrorAction SilentlyContinue ` -Include 'mcp.json','*mcp*.json','settings.json','SKILL.md' | Select-Object FullName, Length, LastWriteTime | Sort-Object LastWriteTime -Descending | Format-Table -AutoSize # then review every server command and every skill source for an .exe / .zip install step Get-ChildItem -Path $paths -Recurse -Force -File -Filter '*.json' -ErrorAction SilentlyContinue | Select-String -Pattern 'releases/download|\.zip|\.exe|Invoke-WebRequest|curl ' | Select-Object Path, LineNumber, Line
N5 — Proxy / web gateway queries
- Dead-drop resolver: HTTP POST to any
*.drpc.orgor public Ethereum/Polygon JSON-RPC host from a user agent that is not a browser. With TLS inspection, match request bodies containingeth_calltogether with the contract address0x1823A9a0Ec8e0C25dD957D0841e3D41a4474bAdcor selector0x3bc5de30. This is the single highest-fidelity network signal in the chain. - Static Lua user agent: match the literal user-agent string
s3klqc1wa6ntg1szvckzi0nt9oumpwy6hhq. Cheap to add, zero legitimate use. - Bare-IP POST: POST requests whose Host header is a raw IPv4 address with a URI matching
/api/or/task/followed by a Base64 token, content typemultipart/form-data. - Registry-to-download pivot: a session that visits an MCP or Skill registry domain and then downloads a
.zipfrom a GitHub release within the same hour, from a host with no prior GitHub release-download history.
N6 — Identity-side follow-up
- For any host with a confirmed or suspected hit, pull IdP sign-ins for that user over the following 30 days and look for session reuse from a new ASN or a new device fingerprint with no fresh MFA interaction. StealC steals cookies, so the attacker arrives already authenticated.
- Review OAuth grants and personal access tokens created or last-used after the suspected execution time, especially source-control, cloud and secrets-manager scopes.
CrowdStrike LogScale CQL Hunt Queries
None of these queries carries an in-query time filter. Set the window with the console time picker; the intended lookback is recorded as a // LOOKBACK: comment in each card. The AI-Skills wave peaked in April 2026, so a 120-day retrospective sweep is more useful than a 7-day one for Q1 to Q4.
Looks for: the one structural fact the whole campaign depends on. A .cmd/.bat launcher runs start <exe> <payload>, so Falcon sees a cmd.exe parent, a child executable sitting in a user-writable path, and a command line whose final token has a non-executable data extension. Legitimate installers do not look like this. FP: a handful of game mod loaders and legacy self-extracting tools; exclude by hashing the child and allowlisting the known-good ones.
// HUNT: SmartLoader launcher chain -- cmd.exe starts a renamed LuaJIT interpreter with a disguised Lua payload // MITRE: T1204.002 | T1059.003 | T1036.005 | CONF: high FP: low COST: low // REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 (Windows) // LOOKBACK: 120d -- the AI-themed wave peaked April 2026 // TUNING: allowlist by SHA256HashData once you have baselined the handful of legitimate // TUNING: interpreter-plus-data-file launchers in your estate (game mod loaders, legacy SFX tools) #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | ParentBaseFileName=/^cmd\.exe$/i | CommandLine=/\.(txt|log|ico|license|class|ui|dat|cfg|bin)\s*$/i | ImageFileName=/\\(Users|ProgramData)\\/i | FileName!=/^(cmd|powershell|pwsh|conhost|reg|findstr|timeout|xcopy|robocopy|attrib|net|net1|tasklist|where|more|type)\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, ImageFileName, CommandLine, SHA256HashData])
Looks for: lua51.dll or a LuaJIT interpreter written into Downloads, Temp, AppData or ProgramData. Both the initial ZIP extraction and the persistence copy pass through here, so this fires twice per infection. FP: game clients, Wireshark, Redis modules and some CAD suites bundle Lua; those install under Program Files, which this query already excludes, but portable copies in Downloads will hit.
// HUNT: LuaJIT runtime or interpreter dropped into a user-writable staging path // MITRE: T1105 | T1036.005 | CONF: high FP: medium COST: low // REQUIRES: PeFileWritten / NewExecutableWritten // LOOKBACK: 120d // TUNING: exclude ContextBaseFileName for sanctioned game launchers and installers // TUNING: (steam.exe, wireshark installers, vendor setup binaries) after baselining one week #event_simpleName=/^(PeFileWritten|NewExecutableWritten)$/ | TargetFileName=/\\(AppData|ProgramData|Downloads|Temp)\\/i | TargetFileName=/\\(lua51\.dll|luajit\.exe|luau\.exe|lua\.exe|lua5[0-9]*\.dll)$/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, TargetFileName, SHA256HashData])
Looks for: the persistence naming convention. Four independent analyses of this loader family recorded task names of the form <BenignProductName>_<3-8 char Base64 loader id>. The first alternation matches the exact families observed; the second, broader clause catches renamed variants. FP: essentially none in the first clause. Genuine Microsoft tasks do not carry a short underscore suffix.
// HUNT: SmartLoader persistence -- scheduled task masquerading as a Microsoft component // MITRE: T1053.005 | T1036.004 | CONF: high FP: low COST: low // REQUIRES: ScheduledTaskRegistered // LOOKBACK: 120d // FALSE POSITIVES: none observed for the named clause; a few vendor updaters append a full GUID // FALSE POSITIVES: rather than a 3-8 char token, which the length bound already excludes #event_simpleName=/^ScheduledTaskRegistered$/ | TaskName=/(SecurityHealthService|SecurityHealthSystray|WindowsErrorRecovery|WindowsErrorReporting|ApplicationExperienceAnalysis|AudioManager|AudioSrv|OfficeClickToRunTask|OfficeTelemetry|MicrosoftEdgeUpdate)_[A-Za-z0-9+=]{3,8}$/i | table([@timestamp, ComputerName, UserName, TaskName, TaskAuthor])
Looks for: the same persistence event from the process side, which catches variants whose task name does not match Q3's list. The discriminator is the combination of a user-writable action path and a data-file argument. FP: software deployment agents and browser updaters register AppData tasks routinely; the text-extension clause removes almost all of them, and the parent-process exclusion removes the rest.
// HUNT: schtasks.exe creating persistence pointing at a user-writable interpreter + data payload // MITRE: T1053.005 | CONF: high FP: medium COST: low // REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 // LOOKBACK: 120d // TUNING: exclude ParentBaseFileName for your deployment stack (ccmexec.exe, IntuneManagementExtension.exe, // TUNING: msiexec.exe, setup.exe) and for signed browser updaters before promoting this to an alert #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | FileName=/^schtasks\.exe$/i | CommandLine=/\/create/i | CommandLine=/(AppData\\Local|AppData\\Roaming|ProgramData)/i | CommandLine=/\.(txt|log|lua|class|ui|ico|dat)/i | ParentBaseFileName!=/^(ccmexec|IntuneManagementExtension|msiexec|setup|GoogleUpdate|MicrosoftEdgeUpdate)\.exe$/i | table([@timestamp, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine])
Looks for: SmartLoader's C2 resolution. It does not hardcode a C2; it calls a Polygon smart contract through a public JSON-RPC provider and reads the returned string. That call is the durable indicator because the operator rotates the C2 by editing one on-chain value, not by rebuilding the loader. FP: crypto wallets, MetaMask helper processes, hardhat/foundry and any node.exe running a web3 project. Exclude by process, not by domain.
// HUNT: dead-drop resolver -- public blockchain RPC resolved by a process that is not a browser or wallet // MITRE: T1102.001 | T1568 | CONF: high FP: medium COST: low // REQUIRES: DnsRequest / SuspiciousDnsRequest // LOOKBACK: 120d // TUNING: exclude ContextBaseFileName for sanctioned wallet and web3 tooling (node.exe on dev // TUNING: subnets, MetaMask host processes, hardhat, foundry, geth). Do NOT allowlist the domain itself. #event_simpleName=/^(DnsRequest|SuspiciousDnsRequest)$/ | DomainName=/(^|\.)(drpc\.org|polygon-rpc\.com|llamarpc\.com|ankr\.com|infura\.io|alchemy\.com|quiknode\.pro|blastapi\.io|publicnode\.com)$/i | ContextBaseFileName!=/^(chrome|msedge|firefox|brave|opera|iexplore|safari|node|geth)\.exe$/i | groupBy([ComputerName, ContextBaseFileName, DomainName], function=count(as=Lookups)) | sort(Lookups, order=desc)
Looks for: the host-profiling step. Two independent analyses of this loader recorded an ip-api.com lookup early in execution. On its own it is weak; correlated with a Q1 or Q3 hit on the same host it is strong. FP: VPN clients, licensing checks and some telemetry SDKs use the same services. Treat a single lookup as enrichment and only escalate when the calling process also appears in another query.
// HUNT: SmartLoader host fingerprinting -- public geo-IP service queried by a non-browser process // MITRE: T1614 | T1082 | CONF: medium FP: medium COST: low // REQUIRES: DnsRequest / SuspiciousDnsRequest // LOOKBACK: 30d // TUNING: exclude your VPN client, EDR/telemetry agents and licensing services by ContextBaseFileName; // TUNING: use this as a correlation signal against Q1/Q3 hits rather than a standalone alert #event_simpleName=/^(DnsRequest|SuspiciousDnsRequest)$/ | DomainName=/^(ip-api\.com|api\.ipify\.org|ipinfo\.io|icanhazip\.com|ifconfig\.me|ipwho\.is|checkip\.amazonaws\.com)$/i | ContextBaseFileName!=/^(chrome|msedge|firefox|brave|opera|iexplore|svchost|teams|slack)\.exe$/i | groupBy([ComputerName, ContextBaseFileName, DomainName], function=count(as=Lookups)) | sort(Lookups, order=desc)
Looks for: the second-stage staging fetch. SmartLoader re-downloads an encrypted Lua blob from an attacker repository on every persistence run, which is what makes the infection survive deletion of the cached stage. FP: high in any engineering estate. This is a triage aid scoped by process, not a standalone alert. Rank by rarity of the calling process on the host, and prioritise processes running from AppData.
// HUNT: SmartLoader second-stage staging -- raw GitHub content pulled by a non-developer process // MITRE: T1102 | T1105 | CONF: medium FP: high COST: medium // REQUIRES: DnsRequest / SuspiciousDnsRequest // LOOKBACK: 30d // TUNING: exclude your full developer toolchain by ContextBaseFileName (git, node, python, pip, go, // TUNING: code, devenv, curl, wget, gh) and every browser; then rank surviving processes by rarity // TUNING: per host. A process seen on one host in the estate is the one worth opening. #event_simpleName=/^(DnsRequest|SuspiciousDnsRequest)$/ | DomainName=/^(raw\.githubusercontent\.com|objects\.githubusercontent\.com|gist\.githubusercontent\.com)$/i | ContextBaseFileName!=/^(git|git-remote-https|gh|code|devenv|node|npm|yarn|python|python3|pip|go|curl|wget|msedge|chrome|firefox|brave|dotnet|java)\.exe$/i | groupBy([ComputerName, ContextBaseFileName], function=count(as=Fetches)) | sort(Fetches, order=desc)
Looks for: the three bare-IP C2 addresses recovered during reverse engineering of this loader family. Confidence is rated medium not because the attribution is weak but because the addresses are last-seen 2026-04-12 and the operator rotates them from the smart contract. Run it as a retrospective sweep, keep Q5 as the forward-looking control.
// HUNT: known SmartLoader / StealC bare-IP C2 (historical -- rotates via the Polygon contract) // MITRE: T1071.001 | T1041 | CONF: medium FP: low COST: low // REQUIRES: NetworkConnectIP4 // LOOKBACK: 120d retrospective sweep #event_simpleName=/^NetworkConnectIP4$/ | in(RemoteAddressIP4, values=["144.31.57.65", "144.31.57.67", "213.176.73.149"]) | table([@timestamp, ComputerName, UserName, ContextBaseFileName, RemoteAddressIP4, RemotePort])
Looks for: the delivery artefact itself. Campaign ZIP names follow the repository's lure, so they carry AI, MCP, Skill, agent or vendor-integration wording. FP: high wherever AI tooling is genuinely in use. Use it to build the target list for the N3 single-line-payload sweep, and to hash every hit against section 10's CSV, not as an alert.
// HUNT: AgentBaiting delivery -- AI/MCP/Skill-themed archive dropped into a user-writable directory // MITRE: T1195.001 | T1608.001 | CONF: medium FP: high COST: low // REQUIRES: ZipFileWritten // LOOKBACK: 120d // TUNING: this is a triage feed, not an alert. Hash every hit against the section 10 CSV first, // TUNING: then exclude ContextBaseFileName for your sanctioned package managers and IDEs. #event_simpleName=/^ZipFileWritten$/ | TargetFileName=/\\(Downloads|Temp|Desktop|AppData)\\/i | TargetFileName=/(mcp|skill|claude|agent|copilot|gemini|chatgpt|openai|llm|ai)[-_]/i | table([@timestamp, ComputerName, UserName, ContextBaseFileName, TargetFileName, SHA256HashData])
Looks for: the AgentBaiting step directly. When an agent surfaces a campaign repository and the operator accepts, the retrieval usually runs as a child of the agent process rather than as a browser download, so it never passes through any browser-download control you have. FP: high wherever agents are sanctioned, because this is also what legitimate agent work looks like. Baseline for a week first, then alert only on retrievals whose target host or repository is outside your approved catalog.
// HUNT: AgentBaiting -- coding-agent process spawns a shell that retrieves or expands remote content // MITRE: T1195.001 | T1105 | T1059 | CONF: medium FP: high COST: medium // REQUIRES: ProcessRollup2 / SyntheticProcessRollup2 // LOOKBACK: 30d // TUNING: baseline one week before alerting. Then keep only retrievals whose target is outside the // TUNING: approved capability catalog, and always keep any hit whose target is a releases/download URL // TUNING: -- a legitimate MCP server or Skill is source plus a manifest, never a downloaded Windows binary. #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | ParentBaseFileName=/^(node|claude|cursor|code|windsurf|codex|gemini|copilot|aider|continue|python|python3)\.exe$/i | FileName=/^(cmd|powershell|pwsh|curl|tar|expand|git)\.exe$/i | CommandLine=/(Invoke-WebRequest|Invoke-RestMethod|Start-BitsTransfer|Expand-Archive|releases\/download|git\s+clone|curl\s+-|tar\s+-x)/i | table([@timestamp, ComputerName, UserName, GrandparentBaseFileName, ParentBaseFileName, FileName, CommandLine])
CrowdStrike Custom IOA Recommendations
Three of the ten queries are strong enough to promote to Custom IOAs. The rest stay hunt/investigate-only because their false-positive profile depends on estate-specific baselining.
IOA-1 — Interpreter launched from a shell with a data-file argument Promote
| Field | Value |
|---|---|
| Rule group | Process Creation (Windows) |
| Action | Detect · Severity High (start at Monitor for 7 days, then raise) |
| Parent image | .*\\cmd\.exe |
| Image filename | .*\\(Users|ProgramData)\\.*\.exe |
| Command line | .*\.(txt|log|ico|license|class|ui|dat|cfg|bin)\s*$ |
| Exclusions | SHA-256 allowlist built from the Q1 baseline; sanctioned game-mod launchers |
| Why it holds | The operator can rename the interpreter and change the payload extension, but not remove the shell-parent plus data-file-argument shape without rewriting the launcher model that every observed variant shares. |
IOA-2 — Scheduled task masquerading as a Microsoft component Promote
| Field | Value |
|---|---|
| Rule group | Process Creation (Windows) — schtasks.exe |
| Action | Detect · Severity High |
| Image filename | .*\\schtasks\.exe |
| Command line | (?i).*/create.*(AppData\\(Local|Roaming)|ProgramData).*\.(txt|log|lua|class|ui|ico|dat).* |
| Exclusions | Parent image matching your deployment stack and signed browser updaters |
| Why it holds | Both persistence paths register a task whose action is an interpreter under AppData taking a text-extension argument. Renaming the task does not change that. |
IOA-3 — Lua runtime written to a user-writable path Promote after baseline
| Field | Value |
|---|---|
| Rule group | File Creation (Windows) |
| Action | Monitor first · raise to Detect · Severity Medium once the estate baseline is clean |
| Target filename | .*\\(AppData|ProgramData|Downloads|Temp)\\.*\\(lua51\.dll|luajit\.exe|luau\.exe) |
| Exclusions | Sanctioned game clients and network-analysis tooling by writing-process image |
| Why it holds | Every documented variant ships the Lua runtime with the payload; the three-file variants link it statically but still drop the interpreter itself. |
Hunt / investigate-only (do not promote)
- Q5, Q6, Q7 — DNS-based. Their FP profile is entirely determined by which developer and wallet tooling you sanction. Run as scheduled searches feeding a triage queue, not as IOAs.
- Q8 — atomic and perishable. Ship the addresses through IOC Management (section 10) instead, where they can expire.
- Q9, Q10 — high FP by design. These are the AgentBaiting visibility layer; their value is inventory and triage, not alerting.
Falcon prevention policy adjuncts
- Enable Suspicious Process Blocking and Script-Based Execution Monitoring in the assigned prevention policy for developer workstations. The Lua stage is not a script Falcon parses, but the shell launcher is.
- Add the section 10 CSV to IOC Management. The three double-sourced ZIP hashes are proposed at
prevent; everything else atdetectso it enriches rather than blocks.
Machine-Readable IOC Appendix
type,value,action,severity,expiration,description,tags sha256,91e5dbfaf45edf25fbc2168f92083e05dfa427afa7633e991392e33cc7427dad,prevent,high,2027-01-28,SmartLoader package awesome-skills-claude-3.3.zip,campaign:AgentBaiting sha256,c15693106682f2ddb26649cab6e1962a64537627cde4c5d3c79d5a0be8c1b5a8,prevent,high,2027-01-28,SmartLoader package mcp-walmart-2.2.zip,campaign:AgentBaiting sha256,66afc7d87d10dbe392898c4e5c613e0442fabb396415c2bef3a5ef2ac752c5ad,prevent,high,2027-01-28,SmartLoader package server_databricks_mcp_1.6.zip,campaign:AgentBaiting sha256,216a2c99fd42c00f9323d8b16dd19f622f7f4778b2b1d7cf07a3de5621fd1546,detect,high,2027-01-28,SmartLoader package yu-ai-agent-1.0-beta.3.zip,campaign:AgentBaiting sha256,498fe8fb806cd0e6685f97fc7d74de769dae5a28cdc821557b7585ad5ad83147,detect,high,2027-01-28,SmartLoader package agent_ai_awesome_skills_2.0.zip,campaign:AgentBaiting sha256,62744baa8077bb8be237647fd78e3bea2ca0932bf4be3d5618600f97118095f8,detect,high,2027-01-28,SmartLoader package for-security-mcp-3.3.zip,campaign:AgentBaiting sha256,1da8df487d30b988f3c350c065206726aaa13f079a07151cd42ab5579994b9de,detect,high,2027-01-28,SmartLoader package plugins_claude_awesome_code_2.4.zip,campaign:AgentBaiting sha256,a33f40cab1ab7f971d3464af3e7595918107332b9e83342007571842b9e22826,detect,high,2027-01-28,SmartLoader package mcp-server-jenkins-3.2.zip,campaign:AgentBaiting sha256,3c858facbad66f5479e2c4add171421dc1b6488b36f33e7cff073aba585954a7,detect,high,2027-01-28,SmartLoader package gateway-docker-mcp-v1.6-alpha.5.zip,campaign:AgentBaiting sha256,fc1278f419e611bf40ca414099bfd9ad98a31ffb054371e8cb65a84849b00eaf,detect,high,2027-01-28,SmartLoader package alibabacloud-skills-bigdata-v1.7.zip,campaign:AgentBaiting sha256,fad3d429172932b72e50f52af169a80439464e3538d97810509090e2e6cdf32a,detect,high,2027-01-28,SmartLoader LuaJIT interpreter loader.exe,campaign:FakeGit sha256,bff0904456e3151221d29ed1d7c88fc31587efbdfb28817cdcb7ec7f20cade21,detect,high,2027-01-28,SmartLoader LuaJIT interpreter selector.exe,campaign:FakeGit sha256,bbd438d3d7a59152f1dd5e45bb8d22ee1c07f95cfe42cebbe756aaf4feadc875,detect,high,2027-01-28,SmartLoader LuaJIT interpreter unit.exe,campaign:FakeGit sha256,3595a6b226ce4daa0a28edea152b3a887c01f6323db1d082f6568c995cdefb55,detect,high,2027-01-28,SmartLoader LuaJIT interpreter luajit.exe,campaign:FakeGit sha256,09e0f7616dfd2f7eb2876f6ef7331d6dbc78775acd594a94b0397a56717d1fcc,detect,high,2027-01-28,SmartLoader bundled lua51.dll runtime,campaign:FakeGit sha256,440ceb0dc5911faca54ed9a4dd186dad3d006ae4f52d0bb7d1e4b4edd8c3693a,detect,high,2027-01-28,SmartLoader bundled lua51.dll runtime,campaign:FakeGit sha256,87de3e5a8ef669589c421220cd392ae8027a8f8d3cd97d35ac339f87dcff12c8,detect,high,2027-01-28,StealC packed PE staged from GitHub,campaign:FakeGit ipv4,144.31.57.65,detect,high,2026-10-28,SmartLoader C2 last seen 2026-04-12 rotates via Polygon contract,campaign:FakeGit ipv4,144.31.57.67,detect,high,2026-10-28,SmartLoader C2 last seen 2026-04-12 rotates via Polygon contract,campaign:FakeGit ipv4,213.176.73.149,detect,high,2026-10-28,StealC C2 last seen 2026-04-12,campaign:FakeGit ipv4,REPLACE_WITH_CURRENT_C2_FROM_CONTRACT_QUERY,detect,high,2026-10-28,live C2 read from the Polygon dead-drop contract at triage time,campaign:FakeGit
# Execution parent=cmd.exe AND child=*.exe under \Users\ or \ProgramData\ AND cmdline ends .txt|.log|.ico|.license|.class|.ui|.dat unsigned GUI-subsystem PE64 with no version metadata, sitting beside lua51.dll single-line file 296-309 KB with a .txt / .log / .ico / .class / .ui extension # Persistence scheduled task named <BenignProductName>_<3-8 char base64 id> two daily tasks registered within minutes of each other on one host task action under %LOCALAPPDATA%\ or %APPDATA%\<base64 id>\ # Command and control JSON-RPC eth_call to a public Polygon/Ethereum RPC host from a non-browser, non-wallet process HTTP POST to a bare IPv4 host, URI /api/<base64> or /task/<base64>, multipart/form-data static user agent s3klqc1wa6ntg1szvckzi0nt9oumpwy6hhq raw.githubusercontent.com fetch by a process running from %APPDATA% or %LOCALAPPDATA% # Collection artefacts cached task file in the user's Pictures folder named after the machine GUID session.lua or debug.lua written to %TEMP%
# Malware SmartLoader LuaJIT-based loader, Prometheus-obfuscated Lua stage StealC infostealer final payload (browser creds, cookies, sessions, extensions, screenshots) Prometheus Lua obfuscator used on the payload # Dead-drop resolver (durable across C2 rotation) polygon.drpc.org public RPC endpoint abused for the eth_call 0x1823A9a0Ec8e0C25dD957D0841e3D41a4474bAdc resolver contract address 0x3bc5de30 function selector returning the C2 string # Second-stage staging https://raw.githubusercontent.com/deepanshugoel99/long/refs/heads/main/long/long/message1.txt https://raw.githubusercontent.com/deepanshugoel99/long/refs/heads/main/long/long/message2.txt # Persistence task names observed across samples SecurityHealthService_ODE3 WindowsErrorRecovery_ODE4 ApplicationExperienceAnalysis_ODE4 WindowsErrorReporting_ODEw AudioManager_ODM3 OfficeClickToRunTask_7d7757 # Renamed LuaJIT interpreters observed luau.exe java.exe luajit.exe lua.exe loader.exe unit.exe selector.exe boot.exe compiler.exe load.exe util.exe # Launcher scripts observed Launcher.cmd Launcher.bat Application.cmd Application.bat App.bat Launch.bat ErrorHandler.cmd application.cmd # Disguised Lua payloads observed module.class import.ui ico64.txt conf.txt proto.txt func.txt package.txt dynasm.txt buff.log static.txt uix.txt x64.txt tree.txt
# For every MCP server and Agent Skill installed in the last 120 days, record and review: # source repository URL, owner handle, commit SHA, version, package SHA-256 # # Reject on sight, no exceptions: # - install step that downloads a .zip or .exe from a Releases page # - README that says to click "More info" then "Run anyway" past SmartScreen # - package containing a .cmd/.bat launcher plus an interpreter binary # - no manifest, no source, only a Windows archive # # Verify the publisher, not the project: # - compare the owner handle character by character against the upstream project owner # - check account age, other repositories, and whether the profile mirrors a real developer # - a registry listing (LobeHub, Glama, MCP.so, MCP Market) is NOT a signal of legitimacy; # those listings reproduce the attacker's own README # # Cross-reference every recorded package hash against the CSV block in this pack.
Placeholder note: the live C2 row ships as REPLACE_WITH_CURRENT_C2_FROM_CONTRACT_QUERY on purpose. The current address exists only as a value inside the Polygon contract and changes whenever the operator updates it. Read it at triage time rather than shipping a guess.
Detection Validation Gates
| Gate | Check | Pass criteria |
|---|---|---|
| 1 · Telemetry ready | Confirm ProcessRollup2, PeFileWritten, ZipFileWritten, ScheduledTaskRegistered, DnsRequest and NetworkConnectIP4 are all present for the developer-workstation host group over the last 24 hours | All six event types return rows. If ZipFileWritten or ScheduledTaskRegistered is empty, Q3, Q4 and Q9 are blind and must be marked as such in the coverage map before the hunt is called complete. |
| 2 · Benign baseline | Run Q1, Q2, Q3 and Q5 across a 30-day window before promoting anything | Q1 and Q3 should return single-digit results in a typical estate. If Q1 returns hundreds, an interpreter-plus-data-file pattern is in legitimate use and must be allowlisted by hash before IOA-1 is enabled. |
| 3 · Positive test — execution chain | On an isolated test host, create a directory containing a renamed copy of a benign LuaJIT interpreter and a .txt file, plus a one-line .cmd running start interp.exe payload.txt. Execute the .cmd. | Q1 returns the event within the normal ingest delay. This validates the parent/child/extension logic without any malicious code. |
| 4 · Positive test — persistence | On the same test host, register a scheduled task named SecurityHealthService_TEST01 whose action is a binary under %LOCALAPPDATA% with a .txt argument | Q3 and Q4 both return the event. Remove the task afterwards. |
| 5 · Positive test — resolver | From a non-browser process on the test host, resolve a public Polygon RPC hostname | Q5 returns the lookup and correctly attributes it to the calling process. |
| 6 · Tuning applied | Every query rated FP medium or higher carries a // TUNING: or // FALSE POSITIVES: line inside the query block, and the exclusion has been populated with real values from your estate | Q2, Q4, Q5, Q6, Q7, Q9, Q10 all have estate-specific exclusions filled in, not just the shipped placeholders. |
| 7 · Promotion | IOA-1 and IOA-2 run in Monitor mode for 7 days with zero unexplained hits before being raised to Detect | Zero unresolved detections across the monitoring window; every hit either confirmed malicious or added to the exclusion list with a written justification. |
| 8 · Provenance | Every atomic indicator shipped in section 10 traces to a saved source snapshot | check_ioc_provenance.py exits 0 against AgentBaiting-SmartLoader-Hunt-sources/. |
Hardening — Tiered
This campaign never exploits anything. It publishes convincing repositories and waits for discovery to do the work, so the controls that matter are the ones that break the discovery-to-execution path rather than the ones that patch a vulnerability.
H-I1 · Block unsigned executables running from user-writable paths on developer workstations
MITRE M1038 (Execution Prevention) · M1045 (Code Signing). Authority: CIS Microsoft Windows 11 Enterprise Benchmark §18 (AppLocker/WDAC), Microsoft Security Baseline for Windows 11. The entire chain runs an unsigned interpreter from %USERPROFILE%, %LOCALAPPDATA% or %APPDATA%. A WDAC or AppLocker publisher rule in audit mode costs nothing and immediately shows you every candidate.
Verify: Get-AppLockerPolicy -Effective -Xml, or for WDAC check event log Microsoft-Windows-CodeIntegrity/Operational for 3076/3077.
H-I2 · Enable ASR rule "Block executable files from running unless they meet a prevalence, age, or trusted list criterion"
MITRE M1038. Authority: Microsoft Defender ASR documentation; CIS Windows 11 Benchmark §18.10.43. GUID 01443614-cd74-433a-b99e-2ecdc07bfc25. The campaign's interpreters are freshly compiled with regular refreshes, so they have no prevalence and no age. Deploy in Audit for one week, then Block on the developer ring.
Verify: Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Ids.
H-I3 · Publish a rejection rule for capability packages, and tell people it exists
MITRE M1051 (Update Software) is the wrong lever here; the right one is M1017 (User Training) paired with a hard technical rule. A legitimate MCP server or Agent Skill is source code with a manifest. It is never a Windows ZIP containing a launcher script, a renamed runtime and a payload disguised as a text file. Any capability whose install step is "download the archive and run the executable" is rejected on sight, no exceptions and no manager override.
Verify: run the N4 inventory script fleet-wide and confirm zero installed capabilities have a .zip or .exe install step.
H-I4 · Add the section 10 CSV to Falcon IOC Management
MITRE M1031 (Network Intrusion Prevention). Three hashes at prevent, the rest at detect, with the stated expirations so the perishable network indicators age out on their own.
Verify: re-run Q8 after import and confirm any historical hit now also raises an IOC detection.
H-N1 · Build a curated catalog of reviewed Skills, MCP servers and agent plugins
MITRE M1016 (Vulnerability Scanning) and M1051, applied to the capability supply chain. Authority: NIST SP 800-161r1 (C-SCRM) §3.1; CSA guidance on MCP registry vetting. The campaign depends entirely on open discovery. A curated internal catalog removes its audience, human and agent alike, and gives teams a fast sanctioned path so the control does not get routed around. Point every agent's default capability source at the catalog.
Verify: agent configuration files reference only catalog sources; N4 output shows no unlisted entries.
H-N2 · Require an isolated evaluation environment for every new capability
MITRE M1048 (Application Isolation and Sandboxing). Authority: NIST SP 800-161r1 §3.4. Evaluate with no browser sessions, no cloud credentials, no SSH keys and no production data present. The campaign's packages fail this test in seconds because there is nothing inside them that does what the README claims.
Verify: the evaluation VM template has no credential material; confirm by running a secrets scanner against the golden image.
H-N3 · Egress policy for blockchain RPC and raw code-hosting content
MITRE M1037 (Filter Network Traffic). Block or explicitly allowlist public Ethereum/Polygon JSON-RPC endpoints from the corporate estate; outside wallets and blockchain development the legitimate footprint is close to zero, and blocking it breaks the dead-drop resolver outright. Restrict raw.githubusercontent.com to hosts with a documented developer need.
Verify: from a standard workstation, attempt a POST to a public Polygon RPC host and confirm the proxy denies it.
H-N4 · Treat agent-initiated retrievals as a first-class monitored path
MITRE M1047 (Audit). Agent-initiated downloads, git clone calls and shell commands bypass browser-download controls entirely because no browser is involved. Ship agent CLI transcripts and shell history to the SIEM, and give changes to Skill directories and MCP configurations the same review weight as a browser download.
Verify: confirm Q10 returns rows for a deliberate agent-initiated git clone on a test host, and that the agent transcript for the same action is searchable in the SIEM.
H-N5 · Inventory every AI capability in use, with hashes
MITRE M1016. Record source repository, owner, commit, version and package hash. Without it, a newly published list of campaign repositories cannot be matched against your estate at all. With it, the match takes minutes.
Verify: pick three random workstations and confirm every installed capability appears in the register with a hash.
H-S1 · Least agency, not just least privilege, for agent tooling
MITRE M1026 (Privileged Account Management) extended to non-human actors. Treat every MCP tool description as untrusted input on the same footing as a web page. Constrain what an agent is permitted to do (install, execute, network-fetch) separately from what it is permitted to read. ⚠ best-practice, no formal benchmark — MCP-specific hardening standards are still emerging; the CSA research note and vendor guidance are the current best authority.
H-S2 · WDAC in enforced mode on developer workstations
MITRE M1038, M1045. Authority: Microsoft WDAC deployment guidance; DISA Windows 11 STIG WN11-00-000040. Moving H-I1 from audit to enforcement is the control that ends this class of attack rather than detecting it. It needs a genuine application-inventory project first, which is why it sits here rather than in the immediate tier.
H-S3 · Short-lived, phishing-resistant credentials and token binding
MITRE M1032 (Multi-factor Authentication), M1017. StealC takes live sessions, so the mitigation that actually reduces impact is making a stolen cookie worthless: token binding or device-bound session credentials, short session lifetimes for high-value applications, and continuous access evaluation on the IdP.
Verify: replay a captured session cookie from an unmanaged device in a controlled test and confirm the IdP rejects it.
H-S4 · Browser session and secret hygiene on developer endpoints
MITRE M1027 (Password Policies), M1017. Move cloud and source-control credentials out of browser profiles and local config files into a broker that issues short-lived tokens. This directly reduces what StealC can take on a successful run.
Deployable Playbooks
P1 — AppLocker audit rule for unsigned executables in user-writable paths (H-I1)
Prerequisites: Windows 10/11 Enterprise or Education; Application Identity service (AppIDSvc) set to Automatic; local admin or GPO edit rights; a test ring of no more than 20 developer workstations. Reboot required: No, but the Application Identity service must be running and a gpupdate is needed. Rollback: delete the GPO link (or run Set-AppLockerPolicy -XmlPolicy <empty-policy>.xml with a policy containing no rule collections), then gpupdate /force. Audit-mode rules never block, so rollback restores logging volume only, not user access.
# 1. Ensure the Application Identity service is running (required for AppLocker)
Set-Service -Name AppIDSvc -StartupType Automatic
Start-Service -Name AppIDSvc
# 2. Generate a baseline from the test ring so you can see what would be blocked
$rules = Get-ChildItem -Path "$env:USERPROFILE","$env:LOCALAPPDATA","$env:APPDATA" `
-Recurse -Include '*.exe','*.dll' -Force -ErrorAction SilentlyContinue |
Get-AppLockerFileInformation |
New-AppLockerPolicy -RuleType Publisher,Hash -User Everyone -Optimize
# 3. Export, set every rule collection to AuditOnly, and review before enforcing
$rules | Set-AppLockerPolicy -XmlPolicy 'C:\Temp\AppLocker-Audit.xml'
# 4. Review what fired after 7 days
Get-WinEvent -LogName 'Microsoft-Windows-AppLocker/EXE and DLL' |
Where-Object { $_.Id -in 8003,8004 } |
Select-Object TimeCreated, Id, Message -First 100
P2 — Enable the ASR prevalence/age rule in Audit, then Block (H-I2)
Prerequisites: Microsoft Defender Antivirus in active mode (not passive) with cloud-delivered protection enabled; Windows 10 1803+ or Windows 11; Defender for Endpoint licence for centralised reporting. If Defender runs in passive mode alongside Falcon, ASR rules do not apply and this playbook does not apply either. Reboot required: No. Rollback: run the same command with -AttackSurfaceReductionRules_Actions Disabled, or Remove-MpPreference -AttackSurfaceReductionRules_Ids <GUID>. Takes effect immediately, no reboot.
# Step 1 - Audit for 7 days on the developer ring
Add-MpPreference -AttackSurfaceReductionRules_Ids '01443614-cd74-433a-b99e-2ecdc07bfc25' `
-AttackSurfaceReductionRules_Actions AuditMode
# Step 2 - Review what would have been blocked
Get-WinEvent -LogName 'Microsoft-Windows-Windows Defender/Operational' |
Where-Object { $_.Id -eq 1122 } |
Select-Object TimeCreated, Message -First 100
# Step 3 - After a clean audit window, switch to Block
Set-MpPreference -AttackSurfaceReductionRules_Ids '01443614-cd74-433a-b99e-2ecdc07bfc25' `
-AttackSurfaceReductionRules_Actions Enabled
# Verify
Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Ids
Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Actions
P3 — Fleet sweep and clean removal of SmartLoader persistence
Prerequisites: local admin on the target host; PowerShell 5.1+; run the read-only sweep first and review its output before running the removal block. Do not run removal on a host that is still under investigation, since it destroys the persistence artefacts. Reboot required: No. Rollback: none available for the removal block by design, which is why the sweep is a separate step. Export the sweep output to a file before removal so the artefacts are preserved as evidence; restoring a deleted malicious task is never desirable, but the evidence is.
# ---- STEP 1: SWEEP ONLY (read-only, safe to run fleet-wide) ----
$hits = @()
Get-ScheduledTask | ForEach-Object {
$t = $_
foreach ($act in ($t.Actions | Where-Object { $_.Execute })) {
if ($act.Execute -match '(?i)\\(AppData|ProgramData)\\' -and
$t.TaskName -match '^[A-Za-z]+_[A-Za-z0-9+=]{3,8}$') {
$hits += [pscustomobject]@{
Host = $env:COMPUTERNAME; Task = $t.TaskName; Path = $t.TaskPath
Exec = $act.Execute; Args = $act.Arguments; Author = $t.Author
}
}
}
}
$hits | Export-Csv -NoTypeInformation "C:\Temp\smartloader-sweep-$env:COMPUTERNAME.csv"
$hits | Format-Table -AutoSize
# ---- STEP 2: PRESERVE EVIDENCE (run before removal) ----
foreach ($h in $hits) {
$dir = Split-Path -Parent $h.Exec
if (Test-Path $dir) {
Compress-Archive -Path $dir -DestinationPath "C:\Temp\evidence-$($h.Task).zip" -Force
Get-ChildItem -Path $dir -Force | Get-FileHash -Algorithm SHA256 |
Export-Csv -NoTypeInformation -Append "C:\Temp\smartloader-hashes-$env:COMPUTERNAME.csv"
}
}
# ---- STEP 3: REMOVE (only after evidence capture and IR sign-off) ----
foreach ($h in $hits) {
Unregister-ScheduledTask -TaskName $h.Task -TaskPath $h.Path -Confirm:$false
$dir = Split-Path -Parent $h.Exec
if (Test-Path $dir) { Remove-Item -LiteralPath $dir -Recurse -Force }
}
# Cached tasking and staged second stages
Remove-Item "$env:TEMP\session.lua","$env:TEMP\debug.lua" -Force -ErrorAction SilentlyContinue
P4 — Proxy rules for the dead-drop resolver and exfiltration path (H-N3)
Prerequisites: a web proxy or secure web gateway with category and destination control; TLS inspection for the body-matching rules (the URL-level rules work without it); an approved exception list for genuine wallet and web3 development hosts before you switch from monitor to block. Reboot required: No. Rollback: set each rule back to Monitor, or delete the rule. Because rule 1 can break genuine blockchain development work, deploy it in Monitor for 7 days and build the exception list from what it sees; keeping the exception list under version control is what makes rollback a one-line revert.
# Rule 1 - Blockchain RPC egress (deploy MONITOR first, then BLOCK)
# Match: destination host matches *.drpc.org, *.infura.io, *.alchemy.com,
# *.ankr.com, *.quiknode.pro, *.llamarpc.com, *.publicnode.com,
# polygon-rpc.com
# Except: hosts in the approved web3-development group
# Action: BLOCK, log, alert to the hunt queue
# Rule 2 - Dead-drop contract call (requires TLS inspection)
# Match: method=POST AND body contains "eth_call"
# AND body contains "0x1823A9a0Ec8e0C25dD957D0841e3D41a4474bAdc"
# OR body contains "0x3bc5de30"
# Action: BLOCK, high-severity alert -- this is the highest-fidelity network signal in the chain
# Rule 3 - Static Lua user agent
# Match: User-Agent equals s3klqc1wa6ntg1szvckzi0nt9oumpwy6hhq
# Action: BLOCK, high-severity alert
# Rule 4 - Bare-IP exfiltration pattern
# Match: method=POST AND Host header is a raw IPv4 literal
# AND uri matches ^/(api|task)/[A-Za-z0-9+/=]{8,}$
# AND content-type contains multipart/form-data
# Action: BLOCK, high-severity alert
# Rule 5 - Raw code-hosting content for non-developers
# Match: destination host in (raw.githubusercontent.com,
# objects.githubusercontent.com, gist.githubusercontent.com)
# Except: hosts in the approved developer group
# Action: MONITOR, feed the hunt queue (blocking this estate-wide breaks real work)
Containment Runbook
Sequence matters. StealC exfiltrates live sessions, so credential revocation has to happen even if the endpoint is already isolated, and it has to cover session tokens and OAuth grants, not just passwords.
| Phase | Actions | Owner | Evidence to capture |
|---|---|---|---|
| 0 · Triage 0-30 min |
Confirm the hit is real: does the process tree show a shell parent, an unsigned interpreter in a user path, and a data-file argument? Check for a second scheduled task on the same host. Pull the ZIP hash and compare against the section 10 CSV. | SOC L1 | Falcon process tree export; scheduled task list; ZIP and payload SHA-256 |
| 1 · Isolate 30-60 min |
Network-contain the host in Falcon. Do not power off; the loader keeps state in memory and the packed StealC image is never written to disk. Snapshot memory if your process supports it. | SOC L2 | Containment timestamp; memory image if captured |
| 2 · Revoke within 2 h — do not defer |
For the affected user: revoke all browser sessions and IdP refresh tokens; revoke OAuth grants; rotate source-control PATs and SSH keys; rotate cloud access keys; rotate any secret present in a local config file or browser profile. Force re-authentication with MFA on every device. | IAM + Cloud | Revocation ticket with token IDs; list of rotated secrets |
| 3 · Scope 2-8 h |
Run Q1 through Q4 fleet-wide over 120 days. Run Q8 as a retrospective sweep. Run the N4 capability inventory across every host belonging to the same team. Identify the source repository and check whether any teammate installed the same capability. | Threat Hunt | Query results; list of affected hosts and users; source repository URL and owner handle |
| 4 · Eradicate 8-24 h |
Run playbook P3 steps 1 and 2 on every affected host, get IR sign-off, then step 3. Remove the downloaded archive and the extracted directory. For any host with confirmed StealC execution, rebuild rather than clean: the loader supports arbitrary in-memory payloads and you cannot enumerate what else ran. | Endpoint Eng | P3 sweep CSV and hash CSV per host; rebuild tickets |
| 5 · Recover 24-72 h |
Release containment only after the host is clean or rebuilt and the user's credentials are fully rotated. Restore the user with new credentials on a clean device. Monitor their IdP sign-ins for 30 days for session reuse from a new ASN or device fingerprint. | SOC + IAM | Clean-host attestation; 30-day IdP monitoring rule ID |
| 6 · Report and prevent 72 h+ |
Report the repository and profile to GitHub and to any registry that lists it. Add the package hash to the section 10 CSV and to IOC Management. Add the capability to the rejected list in the curated catalog. Feed the source repository owner handle into the publisher-verification check so the same lookalike pattern is caught next time. | Threat Intel | Abuse report references; updated catalog and IOC entries |
Detection Coverage Map
| Technique | Behaviour | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1608.001 · Stage Capabilities | Malicious repositories published and indexed | — | — | GAP external; covered by H-N1 catalog |
| T1585.001 · Establish Accounts | Lookalike developer profiles | — | — | GAP external; covered by H-I3 publisher verification |
| T1195.001 · Supply Chain Compromise | Agent or user installs a poisoned capability | Q9, Q10 | — | Partial — high FP, inventory-driven |
| T1204.002 · User Execution | Launcher executed after extraction | Q1 | IOA-1 | Good |
| T1059.003 · Windows Command Shell | One-line start launcher | Q1, Q4 | IOA-1 | Good |
| T1059 · Lua via LuaJIT | Obfuscated Lua executed by a renamed interpreter | Q1, Q2 | IOA-1, IOA-3 | Good |
| T1036.005 · Masquerading | Interpreter renamed; payload given a benign extension | Q1, Q2 | IOA-1, IOA-3 | Good |
| T1105 · Ingress Tool Transfer | Lua runtime and payload staged to disk | Q2 | IOA-3 | Good |
| T1053.005 · Scheduled Task | Two daily tasks with independent recovery paths | Q3, Q4 | IOA-2 | Good |
| T1036.004 · Masquerade Task or Service | Task named after a Microsoft component | Q3 | IOA-2 | Good |
| T1564.003 · Hidden Window | GUI-subsystem PE plus FFI ShowWindow | — | — | GAP — no process-level signal; caught only as a property of the Q1 child |
| T1622 · Debugger Evasion | Anti-debug shellcode in executable memory | — | — | GAP — Falcon memory-scan detections only |
| T1614 / T1082 · Location and System Discovery | Geo-IP lookup and host fingerprint | Q6 | — | Partial — correlation signal only |
| T1113 · Screen Capture | Desktop screenshot via GDI | — | — | GAP — visible only as exfil volume in Q8 / proxy |
| T1102.001 · Dead Drop Resolver | Polygon contract eth_call returns the live C2 | Q5 | — | Good with proxy rule 2; Partial on DNS alone |
| T1102 · Web Service | Encrypted second stages pulled from raw GitHub | Q7 | — | Partial — high FP, process-scoped |
| T1071.001 / T1041 · Web Protocols, Exfil over C2 | Multipart POST to a bare-IP C2 | Q8 | — | Partial — addresses rotate; proxy rule 4 carries the durable half |
| T1620 · Reflective Code Loading | StealC PE decrypted and loaded in memory | — | — | GAP — memory-scan and StealC behavioural detections only |
| T1555.003 / T1539 · Browser Credentials, Session Cookies | StealC harvests credentials, cookies and live sessions | — | — | Partial — Falcon credential-access IOAs plus IdP session-reuse monitoring |
Coverage summary: 8 of 19 mapped techniques are covered well, 6 partially, and 5 are gaps. The gaps cluster in two places, and neither is fixable with process telemetry: the pre-execution reputation problem (repository publication and lookalike accounts) belongs to the curated catalog and publisher verification in section 12, and the in-memory half of the chain (anti-debug, reflective load, screen capture) belongs to Falcon's memory-scanning and behavioural layers rather than to a query. The execution and persistence steps in between are the ones this pack covers hard, and they sit on the only path the operator cannot avoid.
Validation gates for all of the above are in section 11. Do not promote IOA-1 or IOA-2 past Monitor until gates 2, 3, 4 and 7 pass in your own estate; Q1's false-positive floor depends entirely on whether any legitimate interpreter-plus-data-file launcher exists in your image.
Hunt Summary Ticket
TITLE: AgentBaiting / FakeGit -- SmartLoader to StealC via fake AI Skills and MCP servers
SEVERITY: High (active campaign, ~7,600 repositories, 14M+ measured downloads)
SCOPE: Windows developer, analyst and AI-agent-enabled workstations. 120-day retrospective.
HYPOTHESIS: A user or an AI coding agent discovered a repository posing as an AI Skill or MCP
server, downloaded its ZIP, and ran the bundled launcher. That launcher started a
renamed LuaJIT interpreter against an obfuscated Lua payload disguised with a benign
extension, which established two masquerading scheduled tasks under %LOCALAPPDATA%,
resolved its C2 from a Polygon smart contract, and loaded StealC in memory.
QUERIES: Q1 launcher chain: cmd.exe parent + interpreter in user path + data-file argument
Q2 lua51.dll / LuaJIT interpreter written to a user-writable path
Q3 scheduled task named <MicrosoftComponent>_<base64 loader id>
Q4 schtasks.exe /create pointing into AppData with a text-extension argument
Q5 blockchain RPC resolved by a non-browser, non-wallet process
Q6 geo-IP lookup from a non-browser process
Q7 raw GitHub content fetched by a non-developer process
Q8 known SmartLoader / StealC bare-IP C2 (retrospective)
Q9 AI/MCP/Skill-themed archive dropped to a user directory
Q10 coding agent spawning a shell that downloads, clones or expands content
DO FIRST: 1. Run Q1 and Q3 over 120 days. These two carry the pack.
2. Run N4 (capability inventory) on every AI-agent-enabled workstation.
3. Import the section 10 CSV into Falcon IOC Management.
4. Enable the ASR prevalence/age rule in Audit on the developer ring (P2).
FINDINGS: [ ] Q1 hits: ______ [ ] Q3 hits: ______ [ ] Q2 hits: ______
[ ] Unvetted capabilities found by N4: ______
[ ] Hosts requiring rebuild: ______
GAPS: Anti-debug shellcode, reflective StealC load and screen capture have no process
telemetry -- memory-scan detections only. Repository publication and lookalike
account creation are external and covered by the curated catalog (H-N1) plus
publisher verification (H-I3), not by any query. Agent CLI transcripts are the
single biggest visibility gap: without them Q10 sees the shell but not the intent.
ACTIONS: [ ] Isolate + revoke sessions and OAuth grants on any confirmed host (containment 1-2)
[ ] Rebuild any host with confirmed StealC execution -- do not clean
[ ] Stand up the curated capability catalog (H-N1)
[ ] Report repository and profile to GitHub and to every registry listing it
OWNER: Threat Hunt / SOC
VERSION: v0.1 -- 2026-07-28
Changelog
References
| Tier | Source | Used for | Accessed |
|---|---|---|---|
| 1 | Island — AgentBaiting: How 800+ Fake AI Skills and MCP Servers Delivered Malware | Primary research. Campaign scale, AgentBaiting technique, ZIP internals, 10 ZIP SHA-256 pairs, defensive guidance | 2026-07-28 |
| 1 | Hexastrike — Cloned, Loaded, and Stolen: 109 Fake GitHub Repositories Delivered SmartLoader and StealC | Loader reverse engineering, Polygon dead-drop resolver, C2 addresses, persistence design, staged StealC PE, component hashes | 2026-07-28 |
| 1 | Centri — JIT Happens: Exposing LuaJIT Malware In The Wild | Independent dynamic analysis: geo-IP check, Base64 loader-ID staging directories, task caching, static Lua user agent | 2026-07-28 |
| 1 | AhnLab ASEC — Distribution of SmartLoader Malware via GitHub Repository Disguised as a Legitimate Project | Four-file ZIP layout, persistence copy path, scheduled task names, C2 tasking schema | 2026-07-28 |
| 2 | Intellibron — Lua-JIT SmartLoader: Analyzing the GitHub Campaign Delivering Stealer | Second confirmation of the scheduled task names and the launcher/interpreter/payload triple | 2026-07-28 |
| 2 | Cloud Security Alliance Labs — research note on FakeGit, AgentBaiting and the MCP supply chain | Ecosystem context: registry vetting failures, scale of the MCP and Skill ecosystem | 2026-07-28 |
| 2 | BleepingComputer — FakeGit campaign uses 7,600 GitHub repos to push SmartLoader malware | Independent restatement of scale and technique | 2026-07-28 |
| 2 | The Hacker News — FakeGit Campaign Uses 7,600 GitHub Repositories to Spread SmartLoader Malware | Historical link to the earlier Lumma-era operation tracked as Water Kurita | 2026-07-28 |
| 2 | CyberSecurityNews — AgentBaiting Campaign Uses 800 Fake AI Skills and MCP Servers | Second independent reproduction of three ZIP SHA-256 values, which is what supports the prevent action on those three rows | 2026-07-28 |
| 2 | GBHackers — AgentBaiting Uses Fake AI Skills and MCP Servers to Deliver SmartLoader and StealC | Corroboration of the execution chain summary | 2026-07-28 |
| 2 | Help Net Security — AI agents tricked into recommending malicious GitHub repositories | Corroboration of the AgentBaiting testing results | 2026-07-28 |
| 2 | DevOps.com — FakeGit Targets AI Coding Agents with Malicious GitHub Repos | Developer-workflow framing | 2026-07-28 |
| 2 | TechNadu — AgentBaiting: FakeGit Weaponizes 800+ Fake AI Skills for SmartLoader | Registry-distribution detail and vendor guidance on treating tool descriptions as untrusted input | 2026-07-28 |
| Framework | MITRE ATT&CK | Technique and mitigation identifiers throughout sections 6 and 12 | 2026-07-28 |
Source snapshots for every entry above are saved alongside this file in AgentBaiting-SmartLoader-Hunt-sources/. Every atomic indicator in section 10 was mechanically traced back to those snapshots before publication.