Kernel v1 UUIDs disclose the host's real NIC MAC and exact wall-clock generation time to any unprivileged user (RFC 4122 sec 6 privacy exposure)
| Field | Value |
|---|---|
| ID | DF-2932 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N |
| CWE | CWE-359 |
| File | sys/kern/kern_uuid.c |
| Lines | 86-91, 99-109, 160-183 |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | kernleak |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
kern_uuidgen() mints version-1 UUIDs whose timestamp field is the raw wall clock (nanotime) and whose node field is the first ethernet interface's real MAC with only the multicast bit ORed into byte 0 β which any observer clears to recover the exact hardware address. RFC 4122 Β§6 Security Considerations calls out precisely this exposure; modern implementations default to random node IDs. sys_uuidgen() has no privilege or prison check, so jailed processes learn the host MAC, and the MAC+timestamp persist into on-media artifacts (hammer2 PFS ids via kern_uuidgen at hammer2_ioctl.c:902; disklabel64 d_stor_uuid) and every uuidgen(1) output β device tracking and artifact-creation-time disclosure. VERIFIED: uid 1001 uuidgen(2) UUIDs are v1 and the decoded timestamp equals the host wall clock to the second, 3/3 runs; the MAC branch proven at helper level (tap0 arpcom-first returns its exact MAC). Fix: random node ids (RFC 4122-conformant), never the host MAC.
Timeline
- 2026-09-02 Discovered during pass-2 audit of kern_uuid.c (GLM 5.3); reproduced unprivileged.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2932 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| uuidleak.c | β | 2.3 KB | view raw | |
| build.sh | β | 117 B | view raw | |
| run.sh | β | 122 B | view raw | |
| build.log | β | 9 B | view raw | |
| run.log | β | 612 B | view raw | |
| run.determinism.log | β | 420 B | view raw | |
| module.tap.log | β | 692 B | view raw | |
| env.txt | β | 345 B | view raw | |
| VERDICT.md | β | 3.0 KB | β raw | |
| fix.diff | β | 398 B | view raw | |
| manifest.json | β | 917 B | view raw | |
| verdict.json | β | 3.3 KB | view raw |
DF-2932 VERDICT β REPRODUCED (leak / privacy)
Finding: the kernel's v1 UUID generator bakes two pieces of sensitive
information into every UUID it hands out via the unprivileged, jail-visible
uuidgen(2) syscall (sys/kern/kern_uuid.c:160-183 β no priv_check, no
prison check):
- Exact wall-clock generation time β
uuid_time()(sys/kern/kern_uuid.c:99-109) usesnanotime(); the returned UUID's 60-bit timestamp is public-format 100 ns ticks since 1582-10-15. - The host's primary NIC MAC address β
uuid_node()(sys/kern/kern_uuid.c:86-91) callsif_getanyethermac()for the real MAC and merely ORs the multicast bit into byte 0 (kern_uuid.c:90); an observer clears that single bit and recovers the exact hardware address. RFC 4122 Β§6 (Security Considerations) names exactly this exposure as the reason implementations moved to random node IDs; DragonFly still embeds the MAC.
Reproduction status
- Wall-clock component: reproduced end-to-end, unprivileged (uid 1001) β
run.log and run.determinism.log:
v1 timestamp decodedequalshost wall clockto the second in 3/3 runs, version nibble 1. - MAC component: reproduced at the helper level on the same guest β
module.tap.log (KLD uuidoff.ko, shared with DF-2931's pack): for the
arpcom-first interface tap0 the exact 6 bytes
if_getanyethermac()returns are tap0's real MAC00:bd:e8:3f:01:00;uuid_node()copies those 6 bytes into the UUID node field and ORs 0x01. End-to-end via the syscall requires the first IFT_ETHER interface's driver to be arpcom-first (every classic PCI NIC: em/re/bge/rl/β¦); this guest's first ether is vtnet, whose softc violates the arpcom-first assumption, so on this guest the syscall instead manifests DF-2931 (type-confused softc bytes in the node field). The two findings are the two branches of the same call chain and were proven in a single module dump.
Impact ceiling
Privacy/tracking, not memory corruption: hardware-address identity disclosure (host MAC persists into every v1 UUID artifact β hammer2 PFS ids via kern_uuidgen [sys/vfs/hammer2/hammer2_ioctl.c:902], disklabel64 d_stor_uuid [sys/kern/subr_disklabel64.c:472], uuidgen(1) output) and creation-time metadata for those artifacts. Jailed processes can invoke uuidgen(2) and learn the host MAC even where AF_LINK information is otherwise hidden from the jail. No write primitive β no uid=0 route; this is objective (2): the disclosure genuinely manifests, ceiling = identity + timing metadata.
Fix
Stop deriving the node from the MAC: always use read_random() +
multicast bit (RFC 4122-conformant random node IDs) β fix.diff (applies
cleanly to sys/kern/kern_uuid.c). This also incidentally immunizes the
uuid layer against DF-2931's type-confused read; applying DF-2931's if.c
fix as well is still correct for the helper itself.
fix_status: not_testable β leak-class finding; kernel-rebuild fix validation is mandated only for memory-corruption findings, and the guest was left clean.
Fix verification
not_testablefix.diff authored (random node ids in uuid_node) and verified git-apply-clean against sys/kern/kern_uuid.c; kernel rebuild validation skipped per contract (leak-class, rebuild mandated only for memory corruption) to keep the guest clean.
findings/poc/DF-2932/fix.diff (git apply --check: clean)
Confirmed kernel references
Detail
Evidence (decisive lines)
["run.log β uid 1001, 4Γ v1 UUIDs, 'v1 timestamp decoded' == 'host wall clock'", 'run.determinism.log β timestamp tracks wall clock across 3 runs', 'module.tap.log β conforming tap0: if_getanyethermac returns exact MAC 00:bd:e8:3f:01:00 (the 6 bytes uuid_node places in the node field)', 'VERDICT.md β full narrative and impact ceiling']
PoC changes
No orchestrator seed (new pass-2 finding); trigger shared with DF-2931 (same uuidleak.c), which decodes the RFC4122 v1 timestamp to demonstrate the wall-clock disclosure.
Verified recommended fix
uuid_node(): always use read_random() for the node id (RFC 4122-conformant random node) instead of embedding the host MAC.
Verdict
REPRODUCED. Wall-clock component proven end-to-end as unprivileged uid 1001 (run.log, run.determinism.log: decoded v1 timestamp equals host wall clock to the second, 3/3 runs, version nibble 1). MAC component proven at the helper level on the same guest (module.tap.log): for arpcom-first tap0, if_getanyethermac() returns the interface's exact MAC, which uuid_node() copies into the UUID node field with only the multicast bit ORed (kern_uuid.c:90) β any observer clears that bit and recovers the hardware address; end-to-end via the syscall requires the first IFT_ETHER driver to be arpcom-first (all classic PCI NICs), which this vtnet-only guest lacks (its branch is DF-2931). RFC 4122 sec 6 names this exact MAC exposure; uuidgen(2) is unprivileged and jail-visible (no priv_check/prison check at kern_uuid.c:160), and the MAC/timestamp persist into on-media artifacts (hammer2 PFS ids, disklabel64 stor_uuid).
No comments yet.