--- description: >- Supply chain security assessment and comprehensive defense guide for March 2026 incidents (Trivy/TeamPCP, CanisterWorm, GlassWorm). tags: [security, supply-chain, incident-response, hardening, defense-guide] last_updated: '2026-03-23' priority: 10 --- # Supply Chain Security Assessment (March 2026) ## Scan Results | Check | Status | Detail | |-------|--------|--------| | CanisterWorm persistence | **CLEAN** | No `pgmon` service, no `~/.local/share/pgmon/` | | TeamPCP C2 connection | **CLEAN** | No active connection to `45.148.10.212` | | TeamPCP exfil repos | **CLEAN** | No `tpcp-docs-*` repos found | | ICP C2 connection | **CLEAN** | No active connection to ICP endpoint | | DNS resolution (C2) | **NOTES** | `scan.aquasecurtiy.org` still resolves to `45.148.10.212` (not yet sinkholed) | | DNS resolution (ICP) | **NOTES** | ICP endpoint resolves via Dfinity boundary nodes (expected - decentralized) | ### Note on DNS Resolution C2 domains resolving is expected - they have not been fully sinkholed. The critical check is **active connections**, which are absent. ## Threat Landscape Summary ### Trivy/TeamPCP (March 19-22) - Tag poisoning on `aquasecurity/trivy-action` (75 tags) - Memory-based credential theft via `/proc//mem` - Exfiltration via typo-squatted domain + victim's own GitHub repos - Safe version: `aquasec/trivy:0.69.3@sha256:0d52a20836526e387157a55f9368d4001c95b77c3855a9010049448106a6b5e0` ### CanisterWorm (March 20) - 66+ malicious npm packages via stolen tokens - Self-propagating: harvests npm tokens, republishes victim's packages - Persistence: systemd user service (`pgmon`) - C2: ICP blockchain (resistant to takedown) - Preceded by "Shai Hulud" worm (2025) - now a recurring pattern - Related 2026 campaigns: Shai-Hulud 2.0 (preinstall phase, destructive fallback), SANDWORM_MODE (MCP injection, multi-channel exfil) ### GlassWorm (January-March 2026) - Invisible Unicode chars (U+FE00-FE0F, U+E0100-E01EF) encode payloads - 151+ GitHub repos, 72+ VS Code/Open VSX extensions, 400+ total affected packages - Commits blend in with realistic surrounding changes (AI-generated) - **Cannot be detected by visual code review or standard linting** - Decoder pattern: maps codepoints to byte values, passes to `eval(Buffer.from(...))` --- ## Defense Technologies (Deep Research Findings) ### Layered Detection Architecture ```text Layer 1 (Preventive): Workflow Policy + SHA Pinning + OIDC Layer 2 (Preventive): npm Hardening + Token Scoping + Script Sandboxing Layer 3 (Detective): Runtime Monitoring (eBPF: Falco/Tetragon/Tracee) Layer 4 (Detective): Unicode Scanning + Pre-commit Hooks Layer 5 (Detective): SBOM + Vuln Scanning (syft/grype) + Nuclei Layer 6 (Detective): DNS/Network Monitoring + Threat Intel Feeds Layer 7 (Response): Credential Rotation + Incident Playbook ``` ### 1. GitHub Actions Hardening (Trivy/TeamPCP Defense) #### SHA256 Digest Pinning All Actions MUST be pinned to full commit SHA, never tags or branches: ```yaml # WRONG - vulnerable to tag poisoning - uses: aquasecurity/trivy-action@0.69.3 # CORRECT - immutable - uses: aquasecurity/trivy-action@a12a3943b4151b629d348435e26e7cb35bc0db20 ``` #### OIDC Token Hardening - Enforce short-lived tokens (5-15 min expiration) - Restrict token audience to specific cloud providers and repositories - Use conditional access policies tied to repository, branch, and environment - Disable all long-lived PATs; enforce OIDC for machine accounts #### Environment Protection Rules ```yaml environments: production: protection_rules: - type: required_reviewers reviewers: 2 - type: wait_timer minutes: 30 - type: restrict_to_deployment_keys ``` #### Reusable Workflow Security - Store in private repos with explicit access controls - Require signed commits for workflow updates - Pin calls to specific commits, not branches - Audit log all workflow invocations #### Action Allowlist Enforcement ```yaml # Branch protection or custom CI check approved_actions: - actions/checkout@a1b2c3d4e5f6 - actions/setup-node@f1b2c3d4e5f6 - org/internal-action@abcdef123456 enforcement: reject_if_not_in_list ``` #### Tag Poisoning Detection Verify action repos for suspicious signals: - Tag has been force-pushed (multiple SHAs for same tag) - Repo created < 90 days ago - Zero stars and zero forks - Unknown or compromised maintainers Tools: Snyk GitHub Actions Security (auto-scans workflows), SLSA provenance verification. ### 2. Memory Forensics (TeamPCP `/proc/pid/mem` Defense) #### Attack Vector `/proc/[pid]/mem` access allows reading running process memory to extract credentials, tokens, and secrets. TeamPCP targets CI/CD runners holding temporary credentials during workflow execution. #### Detection Techniques **Volatility 3** - Post-incident memory analysis: ```bash vol3 -f memory.dump linux.psaux # Process list with env vars vol3 -f memory.dump linux.bash # Command history # Scan for credential patterns: ghp_*, ghu_*, AWS_*, npm_* ``` **eBPF Real-Time Monitoring** - Detect access at syscall level: ```c // Trace openat syscall for /proc/*/mem access TRACEPOINT_PROBE(syscalls, sys_enter_openat) { char path[256]; bpf_probe_read_user_str(&path, sizeof(path), (void *)ctx->args[1]); if (strstr(&path, "/proc") && strstr(&path, "mem")) { events.perf_submit(ctx, &event, sizeof(event)); } return 0; } ``` **memdump** - Capture CI/CD runner memory post-execution, scan for credential remnants before process termination. #### Defensive Countermeasures ```yaml # Container runtime - deny ptrace capability securityContext: capabilities: drop: [SYS_PTRACE] readOnlyRootFilesystem: true ``` - Deploy runners in VM isolation (not container-only) - Enable hardware memory encryption (Intel TME / AMD SEV-SNP) - Rotate all credentials immediately post-workflow execution ### 3. eBPF Runtime Security (Cross-Vector Detection) #### Tool Comparison | Tool | Strength | Best For | Enforcement | |------|----------|----------|-------------| | **Falco** | Rules-based alerting | Container environments, broad adoption | Alert only (SIGSTOP可选) | | **Tetragon** | Kubernetes-native, policy enforcement | K8s clusters, tracing policies | Kill/block via policy | | **Tracee** | Deep syscall tracing, eBPF-only | Bare-metal, CI/CD runners | Alert + filter | #### Falco Rules for Supply Chain Attacks ```yaml - rule: Unauthorized Process Memory Access condition: > open and container and fd.name glob "/proc/*/mem" or syscall == ptrace output: "Unauthorized memory access (user=%user.name cmd=%proc.name file=%fd.name)" priority: CRITICAL tags: [supply-chain, memory_access] - rule: Suspicious Env Variable Exfiltration condition: > syscall == execve and container and proc.name in (curl, wget, nc, python) and (proc.args contains "GITHUB_TOKEN" or proc.args contains "ghp_" or proc.args contains "npm_") priority: CRITICAL tags: [supply-chain, credential_exfil] - rule: Suspicious Systemd Service Creation condition: > open_write and fd.name glob "*/.config/systemd/user/*" and not proc.name in (systemctl, systemd, ansible) priority: HIGH tags: [supply-chain, persistence] ``` #### Tetragon TracingPolicy (Enforcement) ```yaml apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: block-proc-mem-access spec: kprobes: - call: "__fput" selectors: - matchArgs: - index: 0 operator: "Equal" values: ["/proc/*/mem"] action: SIGKILL ``` #### Tracee for CI/CD Runner Monitoring ```bash tracee \ --events=open,openat,ptrace,execve \ --filter='container and (event.args.pathname contains "/proc" and event.args.pathname contains "mem")' \ --output json \ --output-file events.json ``` **Workstation Note**: eBPF tools require kernel support. For this Arch Linux workstation, `bpf` package provides headers. Falco can run in agent mode for local monitoring. ### 4. npm Supply Chain Defense (CanisterWorm Defense) #### Lifecycle Script Mitigation ```bash # Global disable (already applied) npm config set ignore-scripts true # Per-project override when needed npm ci --ignore-scripts=false --only=dev # explicit opt-in # Node.js policy-based sandboxing (experimental) node --experimental-policy=policy.json install ``` **Limitation**: Shai-Hulud 2.0 uses **preinstall** phase, bypassing postinstall-focused defenses. `--ignore-scripts` blocks both, but some legitimate packages require scripts. Use allowlists: ```json // policy.json - allow specific packages only { "resources": { "node_modules/.package-lock.json": { "integrity": true } }, "dependencies": { "package:lodash": true, "package:express": true } } ``` #### Token Security | Practice | Implementation | |----------|---------------| | Granular Access Tokens (GAT) | Scope to specific repos + read-only or automation permissions | | Token expiration | Set max 90 days; automate rotation | | Never store in `.npmrc` | Use environment variables or OIDC | | Audit tokens quarterly | `npm token list`, revoke unused | | MFA enforcement | Required for package publish (prevents bypass token abuse) | #### npm OIDC (Long-Term Fix) npm OIDC integration is "very hard to compromise" and would "almost completely erase supply-chain attack surface" (Chainguard, 2026). Enables tokenless authentication for package operations. #### Systemd Service Monitoring (CanisterWorm `pgmon`) ```bash # Detect unknown user services systemctl --user list-units --type=service --all # Audit service files for anomalies ls -la ~/.config/systemd/user/ # Check for services with suspicious exec paths grep -r "ExecStart" ~/.config/systemd/user/ | grep -v "/usr/bin\|/usr/local/bin" # Monitor service file creation (eBPF Falco rule above) ``` #### ICP Blockchain C2 Detection - Decentralized C2 via ICP canisters is resistant to traditional takedown - Monitor for outbound connections to Dfinity boundary nodes from unexpected processes - Network baseline: CI/CD runners should never connect to ICP endpoints - Block ICP boundary node IPs at firewall level for non-ICP workloads #### Worm Containment - **socket.dev**: Detects malicious packages within 2-5 minutes of publication via differential analysis - Monitor all npm packages under org control for unauthorized version publishes - Alert on rapid-succession version publishes (1-3 day windows = suspicious) - Revoke and rotate tokens immediately if compromise suspected ### 5. Unicode Attack Defense (GlassWorm Defense) #### Understanding the Decoder Pattern GlassWorm uses this exact decoder signature: ```javascript const s = v => [...v].map(w => ( w = w.codePointAt(0), w >= 0xFE00 && w <= 0xFE0F ? w - 0xFE00 : w >= 0xE0100 && w <= 0xE01EF ? w - 0xE0100 + 16 : null )).filter(n => n !== null); eval(Buffer.from(s(``)).toString('utf-8')); ``` Detection must target: `eval()` + `Buffer.from()` + codepoint arithmetic + apparent empty/whitespace strings. #### Detection Tools ```bash # Variation Selectors (VS1-VS16) grep -rP "[\x{FE00}-\x{FE0F}]" . --exclude-dir=node_modules --exclude-dir=.git # Ideographic Variation Selectors grep -rP "[\x{E0100}-\x{E01EF}]" . --exclude-dir=node_modules --exclude-dir=.git # Combined with decoder pattern grep -rP "(codePointAt|fromCharCode|Buffer\.from)" . \ --include="*.js" --include="*.ts" --exclude-dir=node_modules ``` Python scanner for byte-level detection: ```python #!/usr/bin/env python3 """Scan files for GlassWorm Unicode injection patterns.""" import sys, pathlib VS_RANGES = [(0xFE00, 0xFE0F), (0xE0100, 0xE01EF)] def scan_file(path): content = path.read_bytes() hits = [] for i, b in enumerate(content): # Check for multi-byte UTF-8 sequences in VS ranges if b >= 0xEF: # Start of 3-byte UTF-8 (VS range) if i + 2 < len(content): cp = int.from_bytes(content[i:i+3], 'big') for start, end in VS_RANGES: if start <= cp <= end: hits.append((i, hex(cp))) return hits for p in pathlib.Path(sys.argv[1]).rglob('*'): if p.suffix in ('.js', '.ts', '.mjs', '.cjs', '.py', '.json'): hits = scan_file(p) if hits: print(f"{p}: {len(hits)} invisible chars found") for offset, cp in hits[:5]: print(f" offset {offset}: {cp}") ``` #### Git Pre-commit Hook ```yaml # .pre-commit-config.yaml repos: - repo: local hooks: - id: no-invisible-unicode name: Block invisible Unicode (GlassWorm defense) entry: python3 scripts/unicode-scanner.py language: script types: [text] ``` #### IDE Visibility Configuration **VS Code** - Show invisible characters: ```json { "editor.renderWhitespace": "all", "editor.unicodeHighlight.ambiguousCharacters": true, "editor.unicodeHighlight.invisibleCharacters": true } ``` **Vim/Neovim**: ```vim set list set listchars+=trail:~,nbsp:␣ " Conceal off for PUA/VS ranges syntax match GlassWorm "[\uFE00-\uFE0F\uE0100-\uE01EF]" cchar=█ ``` #### VS Code Extension Security - Verify extension publisher identity before install - Audit extension update history for suspicious version jumps - Known malicious: `quartz.quartz-markdown-editor` v0.3.0 (March 12, 2026) - 72+ malicious Open VSX extensions in coordinated campaign - Prefer extensions with signed commits and provenance - Review extension permissions (workspace access, terminal access = high risk) #### Semgrep Rules ```yaml rules: - id: glassworm-decoder-pattern patterns: - pattern-either: - pattern: | eval(Buffer.from($FN(...)).toString(...)) - pattern: | $VAR = $V => [...$V].map($W => ($W = $W.codePointAt(0), ...)) message: "Potential GlassWorm Unicode decoder pattern detected" severity: ERROR languages: [javascript, typescript] ``` ### 6. SLSA Provenance & Sigstore Ecosystem #### SLSA Framework (v1.2 Current) | Level | Requirement | This Workstation | |-------|-------------|-----------------| | L1 | Documented build process | Partial (Ansible) | | L2 | Hosted build platform | N/A (local dev) | | L3 | Hardened build + provenance | N/A | | L4 | Hermetic + reproducible | N/A | **Applicability**: SLSA primarily targets CI/CD builds. For this local workstation, focus on L1 (documented process) and verifying SLSA provenance of consumed packages/artifacts. #### Sigstore Components | Component | Purpose | Tool | |-----------|---------|------| | **cosign** | Container image signing/verification | `pacman -S cosign` | | **fulcio** | Certificate authority (issues certs based on OIDC) | Service | | **rekor** | Transparency log (immutable record) | Service | | **sigstore-js** | Node.js client library | `pnpm add @sigstore/sign` | ```bash # Verify a container image was signed cosign verify --certificate-identity=action@github.com \ --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ aquasec/trivy:0.69.3 # Sign an artifact cosign sign --key env://COSIGN_PRIVATE_KEY my-image:latest ``` #### in-toto Attestations in-toto provides a framework for verifying the integrity of the software supply chain. Key concepts: - **Layout**: Defines expected steps, authorized functionaries, and inspection rules - **Attestation**: Metadata about a supply chain step (linked to SLSA provenance) - **Verification**: Client-side check that all steps match the layout Integration with GitHub Actions via `slsa-github-generator`: ```yaml - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0 with: attestation-name: my-attestation ``` ### 7. SBOM Management #### Toolchain ```bash # Generate SBOM (CycloneDX format - preferred for security) syft packages dir:./src -o cyclonedx-json > sbom.json # Alternative: SPDX format (preferred for license compliance) syft packages dir:./src -o spdx-json > sbom.spdx.json # Scan SBOM for vulnerabilities grype sbom:sbom.json --fail-on high ``` #### Format Choice | Format | Best For | Tooling | |--------|----------|---------| | **CycloneDX** | Security scanning, vulnerability matching | grype, Dependency-Track | | **SPDX** | License compliance, legal | SPDX tools, FOSSA | #### Dependency-Track (SBOM Repository) Centralized SBOM storage and continuous monitoring: - Upload SBOMs via API on each build - Continuous vulnerability scanning (NVD, OSV, GitHub Advisories) - Policy evaluation (fail builds on critical vulns) - This workstation: run via Docker for local SBOM management ### 8. Nuclei Template-Based Scanning ```bash # Install go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest # Scan for known supply chain C2 indicators nuclei -t supply-chain/ -u scan.aquasecurtiy.org # Scan local git repos for TeamPCP exfil patterns nuclei -t custom/teampcp-exfil.yaml -target https://github.com/org/repo # Custom template for C2 domain detection cat <<'EOF' > nuclei-teampcp.yaml id: teampcp-c2-detection info: name: TeamPCP C2 Domain severity: critical dns: - name: "{{Hostname}}" matchers: - type: word words: - "45.148.10.212" EOF ``` ### 9. ML-Based & Behavioral Detection (Emerging) #### ML Detection Approaches for Supply Chain Attacks | Technique | Application | Tools/Platforms | |-----------|-------------|-----------------| | **Package behavior modeling** | Detect anomalous postinstall behavior vs. historical baseline | socket.dev (differential analysis), EndorLabs | | **Dependency graph anomaly detection** | Flag sudden dependency additions, version spikes, or transitive changes | Snyk Advisor, Socket.dev risk scores | | **Code embedding similarity** | Detect AI-generated commits that mimic project style (GlassWorm evasion) | CodeQL ML models, GitHub Advanced Security | | **Network behavior profiling** | Detect C2 beacon patterns, unusual outbound from build runners | Elastic ML, CrowdStrike Falcon | | **File system entropy analysis** | Detect encoded payloads (Unicode steganography, encrypted strings) | YARA rules + ML classifiers | #### Behavioral Detection Patterns **Anomalous Package Behavior** (CanisterWorm/Shai-Hulud): - Package executes network calls during install (baseline: most packages don't) - Package writes files outside `node_modules` during install - Package reads environment variables or `.npmrc` during install - Package spawns child processes during install - Detection: compare current package behavior against historical behavior profile **Anomalous Commit Patterns** (GlassWorm): - Commit modifies only whitespace/Unicode but diff shows zero visual changes - Commit timestamp clusters with other suspicious commits across repos (coordinated campaign signal) - Commit author style differs from project norm (AI-generated code detection) - Detection: `git diff --word-diff` to catch invisible char changes; commit metadata clustering **Anomalous Build Behavior** (TeamPCP): - Build process accesses `/proc/*/mem` (never legitimate in CI) - Build process resolves non-whitelisted DNS (tag poisoning C2 indicator) - Build process creates outbound connections to non-cdn endpoints - Detection: Falco rules (above), DNS query logging, network egress allowlists #### Implementation for This Workstation ```bash # Socket.dev CLI - local package risk scoring npm install -g socket-cli socket view express # Risk profile before installing socket scan ./src # Scan dependencies for behavioral anomalies # YARA rule for GlassWorm encoded payloads (high entropy strings) # Install: pacman -S yara cat <<'EOF' > rules/glassworm.yar rule GlassWorm_Unicode_Payload { strings: $decoder = /codePointAt.*0xFE00|codePointAt.*0xE0100/ ascii $eval_buf = /eval\(Buffer\.from/ ascii $vs_range = /\xEF\xB8[\x80-\x8F]/ // UTF-8 for U+FE00-FE0F condition: 2 of them } EOF yara -r rules/glassworm.yar ./src ``` ### 10. DNS & Network Monitoring #### Passive DNS Integration - Subscribe to Farsight Security or RiskIQ passive DNS feeds - Query historical DNS data for infrastructure domains during IR - Detect CNAME chains pointing to attacker-controlled servers #### Threat Intelligence Feeds | Feed | Coverage | Integration | |------|----------|-------------| | URLhaus | Known C2 domains | API, daily dump | | Abuse.ch SSLBL | Malicious SSL certificates | IP blocklists | | VirusTotal | Domain reputation | API | | Custom | Typo variants of internal domains | Local script | #### DNS Monitoring for CI/CD - Log all DNS queries from runners to SIEM - Block non-whitelisted external domains from runners - Alert on edit distance < 2 from known infrastructure domains --- ## Hardening Actions ### Immediate (Already Applied) ```bash npm config set ignore-scripts true ``` ### Recommended for This Workstation ```bash # 1. Install supply chain security tools pacman -S syft grype cosign # SBOM, vuln scan, artifact verify uv tool install semgrep # SAST with Unicode rules # 2. Set up Unicode scanning as pre-commit hook # (see GlassWorm section above for hook config) # 3. Configure IDE visibility # (see GlassWorm section for VS Code/Vim settings) # 4. Audit systemd user services monthly systemctl --user list-units --type=service --all # 5. Network-level C2 blocking (optional) # Block 45.148.10.212 and ICP boundary nodes if not needed ``` ### CI/CD Hardening Checklist - [ ] Pin all Actions to SHA256 (never tags) - [ ] Use OIDC over long-lived secrets - [ ] Block outbound to non-essential domains from runners - [ ] Run sensitive builds in ephemeral environments - [ ] Generate and verify SLSA provenance for built artifacts - [ ] Sign container images with cosign - [ ] Generate SBOM on every build (syft + grype) - [ ] Run Nuclei templates against dependencies - [ ] Enable Unicode scanning in CI pipeline --- ## Detection Patterns ### File System IOCs ```bash # CanisterWorm find ~ -name "pgmon*" -o -name "uv-proxy" -o -name "pglog" 2>/dev/null # Unknown systemd user services ls ~/.config/systemd/user/ | grep -v "default.target" ``` ### Unicode IOCs (GlassWorm) ```bash # VS1-VS16 range grep -rP "[\x{FE00}-\x{FE0F}]" . --exclude-dir=node_modules --exclude-dir=.git # Ideographic Variation Selectors grep -rP "[\x{E0100}-\x{E01EF}]" . --exclude-dir=node_modules --exclude-dir=.git # Decoder function pattern grep -rP "codePointAt.*0xFE00|codePointAt.*0xE0100" . --include="*.js" --include="*.ts" ``` ### Network IOCs ```bash # TeamPCP C2 ss -ntu | grep -E "45.148.10.212" getent hosts scan.aquasecurtiy.org # ICP C2 (unexpected connections to Dfinity) ss -ntu | grep -E "icp0\.io|dfinity" ``` --- ## Workstation-Specific Implementation Priority | Priority | Action | Effort | Impact | |----------|--------|--------|--------| | P0 | Pre-commit hook for Unicode scanning | 30min | Blocks GlassWorm | | P0 | VS Code invisible char visibility | 5min | Visual detection | | P1 | Monthly systemd service audit | 10min | Detects CanisterWorm | | P1 | `npm config set ignore-scripts true` verify | 1min | Blocks script-based attacks | | P2 | syft/grype SBOM scanning | 1hr setup | Continuous vuln monitoring | | P2 | Nuclei C2 template scanning | 30min | Network IOC validation | | P3 | Falco local agent setup | 2hr | Runtime memory/process monitoring | | P3 | cosign artifact verification workflow | 1hr | Build integrity | --- ## References - Upwind Security: Trivy/TeamPCP analysis - EndorLabs: CanisterWorm + SANDWORM_MODE technical analysis - Aikido Security: GlassWorm Unicode campaign tracking - Unit42 (Palo Alto): Shai-Hulud 2.0 npm supply chain attack - Chainguard: npm supply chain hardening and OIDC future - CISA: GitHub Actions Security Hardening Guidance - Cilium/Tetragon: eBPF Security Enforcement documentation - Falco: Runtime security rules and best practices - SLSA v1.2: slsa.dev/spec/v1.2 - Sigstore: sigstore.dev - NIST SSDF v1.1: Secure Software Development Framework