|
1 | 1 | # Supply Chain Attack Demo — Mini Shai-Hulud |
2 | 2 |
|
3 | | -A safe, educational demonstration of a supply chain attack modeled on |
| 3 | +A safe, educational reproduction of the |
4 | 4 | [Mini Shai-Hulud](https://thehackernews.com/2026/05/mini-shai-hulud-worm-compromises.html) |
5 | | -(TeamPCP, May 2026), showing how Hyperlight micro-VM isolation contains it |
6 | | -**even when the guest has realistic capabilities**. |
| 5 | +supply chain attack (TeamPCP, May 2026) running inside a Hyperlight micro-VM. |
7 | 6 |
|
8 | 7 | ## Background |
9 | 8 |
|
10 | 9 | Mini Shai-Hulud compromised 500+ packages across npm, PyPI, and PHP — including |
11 | 10 | TanStack, Mistral AI, Guardrails AI, and AntV — affecting 518M+ cumulative |
12 | | -downloads. The attack used typosquatted/hijacked packages to: |
13 | | - |
14 | | -1. **Steal credentials** — SSH keys, AWS creds, `.env` files, 80+ env vars |
15 | | -2. **Probe cloud metadata** — AWS IMDS, Azure IMDS, GCP metadata (169.254.169.254) |
16 | | -3. **Exfiltrate data** — triple-redundant C2 (custom domain, Session Protocol, GitHub dead drops) |
17 | | -4. **Install persistence** — Claude Code `SessionStart` hooks, VS Code `runOn`, LaunchAgents |
18 | | -5. **Self-propagate** — used stolen npm tokens to poison other packages |
19 | | - |
20 | | -## What this demo does |
21 | | - |
22 | | -A fake typosquatted package (`reqeusts` instead of `requests`) simulates the |
23 | | -attack payload. A victim application imports it, triggering the malicious code. |
24 | | -The victim app also does legitimate work: reads input from the workspace, |
25 | | -fetches data from `example.com`, and writes output. |
26 | | - |
27 | | -**Bare metal** — the attack succeeds: secrets stolen, exfiltrated, persistence |
28 | | -installed. Legitimate work also succeeds. |
29 | | - |
30 | | -**Hyperlight sandbox (scoped)** — the guest has real capabilities (`--mount` |
31 | | -for directory access, `--net-allow example.com` for network). Legitimate work |
32 | | -succeeds, but every malicious action is blocked by Hyperlight's security model: |
33 | | - |
34 | | -- **Credential theft → BLOCKED** — `~/.ssh/id_rsa`, `~/.aws/credentials`, etc. |
35 | | - are outside the mounted directory. The guest has filesystem access, but only |
36 | | - to the scoped directory (no HOME, no `/etc/passwd`). |
37 | | -- **Environment variables → NOT SET** — host environment is never forwarded to |
38 | | - the guest. `AWS_ACCESS_KEY_ID`, `GITHUB_TOKEN`, etc. are absent. |
39 | | -- **C2 exfiltration → BLOCKED** — loopback addresses (127.0.0.0/8) are |
40 | | - **always denied** regardless of network policy. The C2 server at |
41 | | - `127.0.0.1:8080` is unreachable. |
42 | | -- **Cloud metadata → BLOCKED** — link-local addresses (169.254.0.0/16) are |
43 | | - **always denied**. AWS IMDS, Azure IMDS, and GCP metadata at 169.254.169.254 |
44 | | - are all blocked — a hardcoded safety net, not a user-configurable policy. |
45 | | -- **Persistence → BLOCKED** — host dotfiles (`~/.claude/settings.json`, |
46 | | - `~/.bashrc`) are not mounted. The guest's ramfs is destroyed on exit. |
47 | | - |
48 | | -This is **active defense**, not just absence of resources. An empty Docker |
49 | | -container blocks the same attacks, but only because it has nothing to attack. |
50 | | -Hyperlight blocks them because its network policy engine and filesystem sandbox |
51 | | -enforce scoped access even when capabilities are granted. |
| 11 | +downloads. The payload: |
| 12 | + |
| 13 | +1. Steals credentials — SSH keys, AWS creds, `.env` files, 80+ env vars |
| 14 | +2. Probes cloud metadata — AWS IMDS, Azure IMDS, GCP metadata (169.254.169.254) |
| 15 | +3. Exfiltrates to triple-redundant C2 (custom domain, Session Protocol, GitHub dead drops) |
| 16 | +4. Installs persistence — Claude Code `SessionStart` hooks, VS Code `runOn`, LaunchAgents |
| 17 | +5. Self-propagates using stolen npm/PyPI publish tokens |
| 18 | + |
| 19 | +## The demo |
| 20 | + |
| 21 | +A typosquatted package (`reqeusts` instead of `requests`) carries a simulated |
| 22 | +version of this payload. A victim app imports it — triggering the stealer — and |
| 23 | +then does legitimate work: reads input from the workspace, fetches data from |
| 24 | +`example.com`, writes output. |
| 25 | + |
| 26 | +Two runs, same code: |
| 27 | + |
| 28 | +**Bare metal** — credentials stolen, C2 exfiltration sent, persistence |
| 29 | +installed, cloud metadata reached. Legitimate work also succeeds. |
| 30 | + |
| 31 | +**Hyperlight micro-VM** (`--mount ./guest --net-allow example.com`) — |
| 32 | +legitimate work succeeds, every attack phase is blocked: |
| 33 | + |
| 34 | +| Phase | What happens | Why | |
| 35 | +|---|---|---| |
| 36 | +| Credential theft | `~/.ssh/id_rsa`, `~/.aws/credentials` → BLOCKED | `FsSandbox` scopes access to the mounted directory. No HOME, no `/etc/passwd`. | |
| 37 | +| Env var harvesting | `AWS_ACCESS_KEY_ID`, `GITHUB_TOKEN` → NOT SET | Host environment is never forwarded into the guest. | |
| 38 | +| Cloud metadata | 169.254.169.254 → BLOCKED | `NetworkPolicy` hardcodes a link-local (169.254.0.0/16) deny. | |
| 39 | +| C2 exfiltration | 127.0.0.1:8080 → BLOCKED | `NetworkPolicy` hardcodes a loopback (127.0.0.0/8) deny. | |
| 40 | +| Persistence | `~/.claude/settings.json`, `~/.bashrc` → BLOCKED | Host dotfiles are outside the mount. Guest ramfs is destroyed on exit. | |
52 | 41 |
|
53 | 42 | ## Running the demo |
54 | 43 |
|
|
0 commit comments