Commit 35c09eb
fix(security): validate registry content digest to block path-traversal host write (#141)
CRITICAL. A malicious or compromised registry (or a MITM when
A3S_REGISTRY_PROTOCOL=http) could write an arbitrary file to the HOST — and
delete an arbitrary host directory — when an image is pulled, in the DEFAULT
config.
oci-distribution returns the manifest digest as the `Docker-Content-Digest`
HTTP header VERBATIM (no hex/format validation). a3s-box did
`digest.strip_prefix("sha256:").unwrap_or(&digest)` and fed the remainder
straight into `Path::join` at two sinks with no validation:
- registry.rs:357 blobs_dir.join(<digest>) + std::fs::write(manifest_json)
-> `sha256:../../../../etc/cron.d/x` writes the attacker-shaped manifest
JSON to an arbitrary host path (persistent arbitrary file WRITE).
- pull.rs:237 store_dir/tmp/<digest> + remove_dir_all
-> arbitrary host directory DELETE, plus store escape.
`Path::join` does not normalize `..`, and the signature gate is a no-op by
default (SignaturePolicy::Skip), so nothing blocked it. The box runtime often
runs as root, amplifying impact (write to cron.d / authorized_keys -> RCE).
This exceeds the recently-fixed HIGH whiteout host-file *deletion*: here it is a
persistent arbitrary host file *write* with attacker-controlled content.
Fix: `validated_digest_hex` validates the registry-returned digest at the trust
boundary — require canonical `sha256:<64 lowercase hex>` and reject anything
else — applied before the digest is used to build any on-disk path (registry
manifest write, pull temp dir / store key). The config/layer blob path was
already safe (stream_and_verify_blob SHA-256-verifies the body and rejects
non-hex digests before rename).
Test: validated_digest_hex_rejects_path_traversal_and_non_hex (accepts a real
64-hex digest; rejects `sha256:../..`, `sha256:abc/../def`, `/`-prefixed,
uppercase, non-hex, wrong-length, sha512, unprefixed). Neuter-verified on the
KVM server (disabling the predicate makes it FAIL); fmt + clippy clean.
Co-authored-by: Roy Lin <roylin@a3s.box>1 parent ae8991c commit 35c09eb
2 files changed
Lines changed: 71 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
216 | 216 | | |
217 | 217 | | |
218 | 218 | | |
219 | | - | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
220 | 224 | | |
| 225 | + | |
221 | 226 | | |
222 | 227 | | |
223 | 228 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
28 | 51 | | |
29 | 52 | | |
30 | 53 | | |
| |||
352 | 375 | | |
353 | 376 | | |
354 | 377 | | |
355 | | - | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
356 | 382 | | |
357 | | - | |
358 | | - | |
359 | | - | |
| 383 | + | |
360 | 384 | | |
361 | 385 | | |
362 | 386 | | |
| |||
712 | 736 | | |
713 | 737 | | |
714 | 738 | | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
715 | 776 | | |
716 | 777 | | |
717 | 778 | | |
| |||
0 commit comments