Skip to content

Instantly share code, notes, and snippets.

@NorfeldtKnowit
Last active April 7, 2026 07:36
Show Gist options
  • Select an option

  • Save NorfeldtKnowit/e6c6b3f4e0d1d6506feb1a40f680aae0 to your computer and use it in GitHub Desktop.

Select an option

Save NorfeldtKnowit/e6c6b3f4e0d1d6506feb1a40f680aae0 to your computer and use it in GitHub Desktop.
LLM prompt: Investigate axios supply chain attack (March 2026) across repos and machines

LLM prompt: Investigate axios supply chain attack (March 2026) across repos and machines

Axios Supply Chain Attack (March 2026) — Investigation Prompt

You are investigating whether any repositories or machines have been compromised by the axios npm supply chain attack discovered on March 31, 2026. Follow these instructions precisely.

Attack Summary

On March 31, 2026, the npm account of axios maintainer "jasonsaayman" was hijacked. Two malicious axios versions were published containing a dependency on plain-crypto-js@4.2.1, which drops a cross-platform Remote Access Trojan (RAT).

Malicious Package Versions

  • axios@1.14.1 (published 2026-03-31 00:21 UTC)
  • axios@0.30.4 (published 2026-03-31 01:00 UTC)
  • plain-crypto-js@4.2.1 (the RAT dropper, published 2026-03-30 23:59 UTC)
  • @shadanai/openclaw@2026.3.28-2, @shadanai/openclaw@2026.3.28-3, @shadanai/openclaw@2026.3.31-1, @shadanai/openclaw@2026.3.31-2
  • @qqbrowser/openclaw-qbot@0.0.130

Safe axios versions: 1.14.0, 0.30.3, or any version before these malicious ones. Clean fix version: 1.14.2.

RAT Filesystem Artifacts

OS Payload Path
macOS /Library/Caches/com.apple.act.mond
Windows %PROGRAMDATA%\wt.exe and %PROGRAMDATA%\system.bat
Linux /tmp/ld.py

Network Indicators of Compromise (IOCs)

  • C2 server: sfrclak.com:8000
  • C2 endpoints: packages.npm.org/product0 (macOS), packages.npm.org/product1 (Windows), packages.npm.org/product2 (Linux)
  • Attacker email: ifstap@proton.me, nrwise@proton.me

Malware Behavior

The malicious plain-crypto-js package runs a postinstall script (setup.js) that:

  1. Detects the OS
  2. Drops an OS-specific RAT binary/script
  3. Cleans up forensic evidence (renames package.json to package.md, removes postinstall hooks)
  4. The RAT beacons to C2 every 60 seconds and supports: shell command execution, file system enumeration, payload execution, and (on Windows) in-memory DLL loading

Investigation Instructions

Phase 1: Machine-Level Checks (Run Once Per Machine)

These checks determine if the machine itself has been compromised, regardless of current repo state. Run these first.

# 1. Check for RAT payload artifacts
echo "=== RAT Payload Check ==="
# macOS
if [ -f "/Library/Caches/com.apple.act.mond" ]; then
  echo "CRITICAL: macOS RAT payload found at /Library/Caches/com.apple.act.mond"
else
  echo "OK: No macOS RAT payload found"
fi
# Linux
if [ -f "/tmp/ld.py" ]; then
  echo "CRITICAL: Linux RAT payload found at /tmp/ld.py"
else
  echo "OK: No Linux RAT payload found"
fi

# 2. Check npm cache for malicious packages
echo ""
echo "=== npm Cache Check ==="
npm cache ls 2>/dev/null | grep -iE "axios.*(1\.14\.1|0\.30\.4)|plain-crypto-js" && \
  echo "WARNING: Malicious package found in npm cache" || \
  echo "OK: npm cache clean"

# 3. Check for active C2 connections
echo ""
echo "=== Active Network Connections ==="
(lsof -i -nP 2>/dev/null || ss -tunap 2>/dev/null) | grep -iE "sfrclak|8000" && \
  echo "CRITICAL: Active C2 connection detected" || \
  echo "OK: No active C2 connections found"

# 4. Check DNS cache/logs if available
echo ""
echo "=== DNS Check ==="
(dscacheutil -cachedump 2>/dev/null; cat /var/log/syslog 2>/dev/null) | grep -i "sfrclak" && \
  echo "WARNING: DNS resolution to C2 domain found" || \
  echo "OK: No C2 domain DNS activity found"

# 5. Check running processes
echo ""
echo "=== Suspicious Process Check ==="
ps aux | grep -E "com\.apple\.act\.mond|wt\.exe|ld\.py" | grep -v grep && \
  echo "CRITICAL: Suspicious process running" || \
  echo "OK: No suspicious processes found"

Phase 2: Repository Checks (Run Per Repository)

For each repository, run these checks. If you have access to multiple repos, run them in parallel using background agents.

Important: gaxios (Google's HTTP client) is NOT the same as axios. Do not flag gaxios as a finding.

Quick Triage (determines if deeper investigation is needed)

REPO_PATH="<absolute path to repo>"

echo "=== Investigating: $REPO_PATH ==="

# 1. Check if axios exists in any current package.json
echo "--- Current package.json files ---"
find "$REPO_PATH" -name "package.json" -not -path "*/node_modules/*" -exec grep -l '"axios"' {} \;

# 2. Check if axios exists in any lock files (npm, yarn, pnpm, bun, deno)
echo "--- Current lock files ---"
find "$REPO_PATH" \( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" -o -name "bun.lock" -o -name "deno.lock" \) -not -path "*/node_modules/*" -exec grep -l "axios" {} \;

# 3. Check node_modules for axios
echo "--- node_modules check ---"
find "$REPO_PATH" -path "*/node_modules/axios/package.json" -exec grep '"version"' {} \;

# 4. Check for plain-crypto-js (the malicious payload package)
echo "--- plain-crypto-js check ---"
find "$REPO_PATH" -path "*/node_modules/plain-crypto-js" -type d
grep -rl "plain-crypto-js" "$REPO_PATH" --include="package*.json" --include="yarn.lock" --include="pnpm-lock.yaml" --include="bun.lock" --include="deno.lock" 2>/dev/null

# 5. Check Deno config files for axios (deno.json, deno.jsonc, import_map.json)
echo "--- Deno config check ---"
find "$REPO_PATH" \( -name "deno.json" -o -name "deno.jsonc" -o -name "import_map.json" \) -exec grep -l "axios" {} \; 2>/dev/null

# 6. Check for transitive axios dependencies (packages that depend on axios)
echo "--- Transitive axios dependencies ---"
cd "$REPO_PATH"
if [ -f "package-lock.json" ] || [ -d "node_modules" ]; then
  npm ls axios 2>/dev/null | head -30
elif [ -f "yarn.lock" ]; then
  yarn why axios 2>/dev/null | head -30
elif [ -f "pnpm-lock.yaml" ]; then
  pnpm why axios 2>/dev/null | head -30
elif [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
  bun pm ls 2>/dev/null | grep -i axios | head -30
elif [ -f "deno.lock" ]; then
  deno info 2>/dev/null | grep -i axios | head -30
fi

If none of the above return results: the repo is CLEAN — skip to the report.

Deep Investigation (only if axios was found)

REPO_PATH="<absolute path to repo>"

# 1. Check exact axios versions in lock files
echo "--- Installed axios versions ---"
find "$REPO_PATH" -name "package-lock.json" -not -path "*/node_modules/*" -exec sh -c '
  echo "File: $1"
  python3 -c "
import json, sys
with open(sys.argv[1]) as f:
    data = json.load(f)
pkgs = data.get(\"packages\", data.get(\"dependencies\", {}))
for key, val in pkgs.items():
    if \"axios\" in key.lower() and \"gaxios\" not in key.lower():
        ver = val.get(\"version\", \"unknown\")
        print(f\"  {key}: {ver}\")
" "$1"
' _ {} \;

# 2. Check if malicious versions were EVER installed (git history)
echo "--- Git history check for malicious versions ---"
cd "$REPO_PATH"
git log --all -p -S "1.14.1" -- "*/package-lock.json" "*/yarn.lock" "*/pnpm-lock.yaml" "*/bun.lock" "*/deno.lock" "package-lock.json" | grep -B5 -A5 "axios.*1\.14\.1"
git log --all -p -S "0.30.4" -- "*/package-lock.json" "*/yarn.lock" "*/pnpm-lock.yaml" "*/bun.lock" "*/deno.lock" "package-lock.json" | grep -B5 -A5 "axios.*0\.30\.4"

# 3. Check for plain-crypto-js in git history
echo "--- Git history check for plain-crypto-js ---"
git log --all -p -S "plain-crypto-js" -- "*/package*.json" "*/yarn.lock" "*/pnpm-lock.yaml" "*/bun.lock" "*/deno.lock"

# 4. Check for the forensic cleanup signature (package.json renamed to package.md)
echo "--- Forensic cleanup artifacts ---"
find "$REPO_PATH" -name "package.md" -path "*/node_modules/*"

# 5. Check postinstall scripts in node_modules packages
echo "--- Suspicious postinstall scripts ---"
find "$REPO_PATH" -path "*/node_modules/*/package.json" -exec grep -l "setup\.js" {} \; 2>/dev/null | head -20

# 6. Check if axios version range could resolve to malicious version
echo "--- Version range analysis ---"
find "$REPO_PATH" -name "package.json" -not -path "*/node_modules/*" -exec sh -c '
  python3 -c "
import json, sys
with open(sys.argv[1]) as f:
    data = json.load(f)
for section in [\"dependencies\", \"devDependencies\", \"peerDependencies\", \"optionalDependencies\"]:
    deps = data.get(section, {})
    if \"axios\" in deps:
        ver = deps[\"axios\"]
        print(f\"  {sys.argv[1]}: axios {ver} in {section}\")
        # Flag ranges that could resolve to malicious versions
        if any(c in ver for c in [\"^\", \"~\", \"*\", \">=\", \"latest\"]):
            print(f\"    WARNING: Range \\\"{ver}\\\" may have resolved to 1.14.1 or 0.30.4\")
" "$1" 2>/dev/null
' _ {} \;

# 7. Check transitive dependencies that pull in axios
echo "--- Transitive dependency check ---"
cd "$REPO_PATH"
if [ -f "package-lock.json" ] || [ -d "node_modules" ]; then
  echo "npm dependency tree for axios:"
  npm ls axios 2>/dev/null || echo "  (could not resolve full tree — check lock file manually)"
  echo ""
  echo "npm why axios:"
  npm why axios 2>/dev/null || true
elif [ -f "yarn.lock" ]; then
  echo "yarn dependency tree for axios:"
  yarn why axios 2>/dev/null || true
elif [ -f "pnpm-lock.yaml" ]; then
  echo "pnpm dependency tree for axios:"
  pnpm why axios 2>/dev/null || true
elif [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
  echo "bun dependency tree for axios:"
  bun pm ls 2>/dev/null | grep -i axios || true
elif [ -f "deno.lock" ] || [ -f "deno.json" ] || [ -f "deno.jsonc" ]; then
  echo "Deno dependency info for axios:"
  # Check deno.json/deno.jsonc imports map for axios
  find "$REPO_PATH" \( -name "deno.json" -o -name "deno.jsonc" \) -exec grep -A2 "axios" {} \; 2>/dev/null || true
  # Check deno.lock for resolved axios versions
  if [ -f "deno.lock" ]; then
    python3 -c "
import json, sys
with open('deno.lock') as f:
    data = json.load(f)
# Check npm packages in deno.lock
for section in ['packages', 'npm']:
    if section in data:
        items = data[section]
        if isinstance(items, dict):
            for key, val in items.items():
                if 'axios' in key.lower() and 'gaxios' not in key.lower():
                    print(f'  {key}: {val}')
" 2>/dev/null || true
  fi
fi

# 8. Check CI config for any axios installs
echo "--- CI pipeline check ---"
find "$REPO_PATH" \( -name "*.yml" -o -name "*.yaml" \) -path "*/.github/*" -exec grep -l "axios" {} \; 2>/dev/null
find "$REPO_PATH" \( -name "Dockerfile*" -o -name "*.dockerfile" \) -exec grep -l "axios" {} \; 2>/dev/null

Phase 3: Batch Scanning Multiple Repos

To scan all repos in a directory (e.g., ~/repos/):

# List all git repos under a parent directory
find ~/repos -maxdepth 2 -name ".git" -type d | sed 's/\/.git$//' | sort

Then launch one background agent per repo using the Phase 2 checks. Each agent should output a single-line verdict at the end.

Output Format

For each repository, produce a report in this exact format:

## <repo-name>
- **Status:** CLEAN | INVESTIGATE | COMPROMISED
- **Axios dependency:** None | Direct (version) | Transitive (via package)
- **Malicious versions in history:** Yes/No
- **plain-crypto-js found:** Yes/No
- **Action required:** None | [specific actions]

For machine-level checks:

## Machine: <hostname>
- **RAT payload:** Not found | FOUND at <path>
- **npm cache:** Clean | Contains malicious packages
- **C2 connections:** None | ACTIVE
- **Suspicious processes:** None | FOUND
- **Action required:** None | [specific actions]

Remediation (If Compromised)

If ANY check returns positive:

  1. IMMEDIATE: Isolate the machine — disconnect from network
  2. Rotate ALL secrets — npm tokens, SSH keys, cloud credentials, API keys, database passwords. Assume any credential accessible from the machine is compromised
  3. Remove RAT payloads — delete the files listed in the artifacts table above
  4. Kill malicious processeskill -9 any com.apple.act.mond, wt.exe, or ld.py processes
  5. Clean npmnpm cache clean --force, remove node_modules, delete lock files, reinstall
  6. Pin axios — lock to 1.14.2 or 1.14.0 (not ^1.14.0 which could re-resolve)
  7. Block C2 — add sfrclak.com and packages.npm.org to firewall/DNS blocklist
  8. Audit git history — check if any commits were made by the attacker during the compromise window (March 30-31, 2026)
  9. Report — file an incident report and notify your security team

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment