β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2932

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2932 Β· 12 files
FileTypeDescriptionSize
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
VERDICT.md
↓ download 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):

  1. Exact wall-clock generation time β€” uuid_time() (sys/kern/kern_uuid.c:99-109) uses nanotime(); the returned UUID's 60-bit timestamp is public-format 100 ns ticks since 1582-10-15.
  2. The host's primary NIC MAC address β€” uuid_node() (sys/kern/kern_uuid.c:86-91) calls if_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 decoded equals host wall clock to 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 MAC 00: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_testable
baseline reproduced→ patch + rebuild →patched clean

fix.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)
↓ fix.diffper-fix-DF-2932

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).