22
33A safe, educational demonstration of a supply chain attack modeled on
44[ 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.
5+ (TeamPCP, May 2026), showing how Hyperlight micro-VM isolation contains it
6+ ** even when the guest has realistic capabilities** .
67
78## Background
89
@@ -11,37 +12,54 @@ TanStack, Mistral AI, Guardrails AI, and AntV — affecting 518M+ cumulative
1112downloads. The attack used typosquatted/hijacked packages to:
1213
13141 . ** Steal credentials** — SSH keys, AWS creds, ` .env ` files, 80+ env vars
14- 2 . ** Exfiltrate data** — triple-redundant C2 (custom domain, Session Protocol, GitHub dead drops)
15- 3 . ** Install persistence** — Claude Code ` SessionStart ` hooks, VS Code ` runOn ` , LaunchAgents
16- 4 . ** Self-propagate** — used stolen npm tokens to poison other packages
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
1719
1820## What this demo does
1921
2022A fake typosquatted package (` reqeusts ` instead of ` requests ` ) simulates the
2123attack payload. A victim application imports it, triggering the malicious code.
22-
23- ** Bare metal** — the attack succeeds: secrets are stolen, exfiltrated, persistence installed.
24-
25- ** Hyperlight sandbox** — every malicious action is blocked by the micro-VM's
26- default-deny security model:
27- - ** Filesystem is isolated** — the guest runs on its own ramfs (from the CPIO
28- initrd). Host directories are only visible if explicitly mounted with
29- ` --mount ` , and even then access is scoped to that directory with path-escape
30- prevention. The attacker's ` ~/.ssh/id_rsa ` , ` ~/.aws/credentials ` , etc. simply
31- don't exist inside the VM.
32- - ** Environment variables are compile-time only** — the guest kernel only has
33- ` PATH ` and ` LD_LIBRARY_PATH ` (set in ` kraft.yaml ` ). There is no ` --env ` flag;
34- the host's environment is never forwarded. ` AWS_ACCESS_KEY_ID ` , ` GITHUB_TOKEN ` ,
35- etc. are all absent.
36- - ** Networking is opt-in** — without ` --net ` , the guest has zero network access
37- (` socket() ` returns "Function not implemented"). Even with ` --net ` , outbound
38- connections can be restricted to specific hosts via ` --net-allow ` .
39- - ** Persistence is impossible** — the guest's ramfs is destroyed when the VM
40- exits. There is no way to write to the host's ` ~/.claude/settings.json ` or
41- ` ~/.bashrc ` unless the host explicitly mounts those paths.
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.
4252
4353## Running the demo
4454
55+ ### Prerequisites
56+
57+ Install ` pyhl ` (the Hyperlight Python runtime):
58+
59+ ``` bash
60+ cargo install --path host --bin pyhl
61+ ```
62+
4563### Bare metal (attack succeeds)
4664
4765The script creates a temporary HOME with planted fake secrets, starts a C2
@@ -57,12 +75,14 @@ cd demos/supply-chain
5775``` bash
5876cd demos/supply-chain/hl-unikraft
5977
60- # One-time setup
61- just build # Build or pull Unikraft kernel
62- just rootfs # Build rootfs with Python + malicious package
78+ # One-time setup — installs kernel + rootfs, warms Python snapshot
79+ just setup
80+
81+ # Scoped sandbox: mount + network, attack still contained
82+ just run
6383
64- # Run
65- just run # Execute inside the sandbox
84+ # Minimal sandbox: mount only, no network
85+ just run-minimal
6686```
6787
6888### C2 server (standalone)
@@ -80,15 +100,13 @@ supply-chain/
80100├── reqeusts/ # Typosquatted package
81101│ ├── __init__.py # Triggers payload on import
82102│ ├── api.py # Fake requests-like API surface
83- │ └── stealer.py # Attack payload (6 phases)
84- ├── victim_app.py # Legitimate-looking app that imports reqeusts
103+ │ └── stealer.py # Attack payload (7 phases)
104+ ├── victim_app.py # App that imports reqeusts + does legitimate work
85105├── c2_server.py # Fake C2 server (receives exfiltrated data)
86106├── bare-metal/
87107│ └── run.sh # Bare-metal demo (plants secrets, runs attack)
88108└── hl-unikraft/
89- ├── Dockerfile # Rootfs with Python + malicious package
90- ├── kraft.yaml # Unikraft kernel config
91- └── Justfile # Build + run commands
109+ └── Justfile # pyhl-based setup + run commands
92110```
93111
94112## Safety
0 commit comments