
Detection & remediation toolkit for the Miasma / Shai-Hulud worm and CVE-2026-35603 (AI-agent/IDE config injection)
Incident analysis + detection/remediation tooling for the Miasma worm (a Mini Shai-Hulud variant) that injects auto-run payloads into AI-agent / IDE configs and npm/GitHub repos.
⚠️ Disclaimer — no warranty. This is a community defensive toolkit provided "as is", without warranty of any kind (see
LICENSE). The scanner and YARA rules are read-only, butpurge-history.shrewrites git history and the.claude/hooks move files to quarantine — both can alter a repository. Back up first, review what each tool does, and run it at your own risk. A clean scan is not a guarantee of safety: indicators evolve and new variants appear. Use on systems and accounts you are authorized to inspect. Found a gap or a bug? SeeSECURITY.mdandCONTRIBUTING.md.
| File | What it is |
|---|---|
content/incident-report.fr.md / content/incident-report.en.md | Full write-up: how it works, payload deobfuscation, IOCs, eradication |
Scan-Miasma.ps1 | Unified scanner (local + remote), structured JSON + per-repo Markdown report — use this |
iocs.psd1 | Shared indicators (hashes, signatures, bad packages, configs) loaded by the scanner — edit IOCs here |
Expand-MiasmaPayload.ps1 | Static deobfuscator for setup.js — peels packer/char-codes → Caesar → AES-128-GCM and extracts the C2/IOCs (never executes the payload) |
Invoke-MiasmaRotation.ps1 | Secret-rotation checklist — detects which credentials are reachable here and prints the revoke commands (read-only; revokes nothing) |
scan-miasma.sh | Bash port of the local scan for Linux/macOS (cross-platform subset) |
purge-history.sh | Purge malicious files from all git history (filter-repo → filter-branch) + backup |
setup-js.yar | YARA rules for the dropper + launcher configs |
.github/actions/miasma-guard/ | Reusable GitHub Actions composite action — refuses to build if the dropper/launcher is present |
.claude/ | Claude Code hooks — real-time guard/quarantine + /miasma-scan command (see below) |
| Tool | Needed for | Required? |
|---|---|---|
PowerShell 7+ (pwsh) | Scan-Miasma.ps1, Expand-MiasmaPayload.ps1, Invoke-MiasmaRotation.ps1 | required (those tools) |
bash + coreutils (sha256sum/shasum, awk, find) | scan-miasma.sh (Linux/macOS) | the bash port only |
| git | local-repo history checks, purge-history.sh | required |
GitHub CLI (gh, authenticated) | Scan-Miasma.ps1 -Mode Remote | remote scan only |
| npm | remote npm audit (lockfile-only) | optional (auto-skipped if absent) |
| git-filter-repo | preferred history rewrite (falls back to built-in git filter-branch) | optional |
YARA (yara) | setup-js.yar scan | optional |
Install YARA (Windows): winget install VirusTotal.YARA — or choco install yara / scoop install yara.
On Linux/macOS: apt install yara / brew install yara. Verify with yara --version.
Note:
-Mode Localdefaults to scanning$env:USERPROFILEonly. Pass-CodeRootsto cover repos elsewhere, e.g.-CodeRoots E:\Sources,D:\work.
# Scan local machine + local repos (+ CVE-2026-35603 ProgramData)
pwsh -File Scan-Miasma.ps1 -Mode Local
pwsh -File Scan-Miasma.ps1 -Mode Local -CodeRoots E:\Sources,D:\work
# Scan remote GitHub repos (authenticate on the target account first for private repos/secrets)
gh auth login
pwsh -File Scan-Miasma.ps1 -Mode Remote -Owners your-org,your-user -OutReport report.md
# Everything + JSON + Markdown report
pwsh -File Scan-Miasma.ps1 -OutJson findings.json -OutReport report.md
Exit code 1 if any INFECTED finding (CI-friendly).
# Purge history of an infected repo (review PATHS in the script first)
./purge-history.sh /path/to/repo
# then, after reviewing the verification:
git push origin --force --all && git push origin --force --tags
# YARA scan
yara -r setup-js.yar /path/to/scan
# Linux/macOS: local scan (bash port — defaults to $HOME; pass roots to widen)
./scan-miasma.sh
./scan-miasma.sh ~/src /opt/work
# Statically deobfuscate a captured dropper (read-only — never runs it)
pwsh -File Expand-MiasmaPayload.ps1 -Path .github/setup.js # writes layers + iocs.txt to <Path>.deob/
pwsh -File Expand-MiasmaPayload.ps1 -SelfTest # verify the decode/decrypt engine
The deobfuscator unpacks the p,a,c,k,e,d packer wave, decodes the char-code wave, auto-detects
and reverses the Caesar shift (override with -Shift), then decrypts every embedded AES-128-GCM
blob (_b bootstrapper, _p stealer) and scans the recovered code for URLs, IPs, and dead-drop
accounts.
# After eradication: generate a prioritized secret-rotation checklist (revokes nothing)
pwsh -File Invoke-MiasmaRotation.ps1 # detects creds reachable here
pwsh -File Invoke-MiasmaRotation.ps1 -OutReport rotation.md
Use the bundled composite action as the first step of any workflow:
# In another repo (pin to a tag/SHA for supply-chain safety):
- uses: actions/checkout@v4
- uses: jchable/miasma-toolkit/.github/actions/miasma-guard@main
# with:
# full-scan: 'true' # also run Scan-Miasma.ps1 -Mode Local (needs pwsh)
This toolkit runs the same guard on itself via .github/workflows/miasma-ci.yml
(referencing the action by relative path). The guard is wave-agnostic and scoped to the worm's
launcher config files, so it never false-positives on docs that merely mention the IOC string.
.claude/)Real-time protection for machines that run Claude Code in this repo. The hooks reuse
iocs.psd1 (no duplicated indicators) and are wired in .claude/settings.json:
| Hook | Event | Action |
|---|---|---|
Guard-Write.ps1 | PreToolUse (Write/Edit/MultiEdit) | Blocks a write that would inject a worm IOC (cancels the tool call) |
Guard-Bash.ps1 | PreToolUse (Bash) | Blocks worm execution — node .github/setup.js, bun/bunx, a piped bun.sh installer |
Scan-Write.ps1 | PostToolUse (Write/Edit/MultiEdit) | Alerts + quarantines an infected file after it lands |
Session-Sweep.ps1 | SessionStart | Fast repo sweep; reports any pre-existing infection into context |
/miasma-scan | command | On-demand wrapper around Scan-Miasma.ps1 |