docs(security): expand supply chain defense guide with deep research findings
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,730 @@
|
||||
---
|
||||
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/<pid>/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
|
||||
Reference in New Issue
Block a user