DF-3564 / verdict.json
{ "finding_id": "DF-3564", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "SipHash_SetKey (sys/crypto/siphash/siphash.c:69-85) leaves a verbatim 16-byte copy of the callers key in stack local k[2] (line 71) after returning, while the rest of the file wipes even the derived context state (SipHash_End bzero, line 188). Demonstrated in a same-source userland build of the exact audited file: the raw key bytes are found on the dead stack at offset -112 below the caller frame. WireGuard computes SipHash with long-term secret keys on the network-reachable handshake path (wg_noise.c:457,498; wg_cookie.c:136), so the residue appears at attacker-driven rates on kernel thread stacks. No standalone disclosure path exists in-tree, so impact is hardening-only (any future kernel-stack info leak would harvest it); one-line explicit_bzero eliminates it with no behavior change (KAT suite 2111/2111 before and after).", "exploit_chain": "", "evidence": "run.log lines under ############ sip_keyscan (DF-3564) ############: RETAINED: verbatim 16-byte key found on the dead stack, offset -112 below caller frame. fix_run.log: patched copy -> NOT FOUND + RESULT: PASS: 2111 checks, 0 failures. Scanning is call-free (inline byte loop) so the probe cannot clobber the dead frame.", "kernel_refs": [ "sys/crypto/siphash/siphash.c:71", "sys/crypto/siphash/siphash.c:79", "sys/crypto/siphash/siphash.c:188", "sys/net/wg/wg_noise.c:240", "sys/net/wg/wg_noise.c:457", "sys/net/wg/wg_cookie.c:136" ], "poc_changes": "Initial seed sketch upgraded: (1) the naive memcmp(3)-based stack scan self-clobbered the dead SetKey frame (libc call frame landed on it) and always reported NOT FOUND; replaced with an inline call-free byte-compare loop which finds the key at offset -112. (2) scan window reduced to 32KB to stay inside the main-thread stack mapping. (3) correctness harness sip_kat.c written from scratch: 64 in-tree official vectors + independent from-paper reference for both SipHash-2-4 and the wg SipHash-1-3 variant + chunked-update equivalence.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 412, "guest_dirty": 0, "build_cmd": "cd findings/poc/DF-3564 && ./build.sh", "run_cmd": "cd findings/poc/DF-3564 && ./run.sh", "code_hash": "f4a4bb20fb0bb81b95be4bc6dc928435c517e259156ab9c273f754777d22edb3", "notes": "Batch context: pass-2 audit of four crypto primitives; sha1 1859/0, siphash 2111/0, des 8916/0, blake2s 6878/0 KAT checks all PASS in the same run.log (guest /usr/src byte-identical to audited tree, hashes in env.txt). Guest left clean (no kernel changes; userland scratch only).", "recommended_fix": "Add explicit_bzero(k, sizeof(k)) after the last XOR use of k in SipHash_SetKey (see fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "One-line explicit_bzero(k, sizeof(k)) in SipHash_SetKey. Baseline: verbatim key found on dead stack at -112. Patched copy of the same source: NOT FOUND (residue eliminated). Full SipHash KAT suite on the patched copy: 2111/2111 PASS, so hashing behavior is unchanged.", "fix_evidence": "fix_run.log: === DF-3564 fix validation === -> NOT FOUND ... exit=1 (1 = retention GONE = fix effective) and RESULT: PASS: 2111 checks, 0 failures.", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (userland same-source validation; kernel unchanged)" } |