DF-3565 / verdict.json
{ "finding_id": "DF-3565", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "The kernel DES entry point ships with des_check_key=0 (sys/crypto/des/des_setkey.c:65) and nothing in-tree ever enables it, so des_set_key() (des_setkey.c:142-153) always takes the unchecked path: the 16-entry weak/semi-weak table and parity validation that the file correctly implements are dead code by default. Demonstrated live: des_set_key(0x0101010101010101) returns 0 (accepted) with the stock default, and returns -2 once the default is flipped. The detection machinery and the key schedule themselves verified correct (weak table exact vs 4096 random + 16 near-miss negatives; 2666 schedules ECB cross-matched against LibreSSL 3.6.1 both directions). Reachability is privileged-only (root-configured cryptoapi SAs / legacy smb_crypt NTLM-derived keys), an attacker cannot choose the key, so impact is a fail-open validation default on a legacy algorithm rather than an exploitable primitive.", "exploit_chain": "", "evidence": "run.log, default policy section: des_set_key(0x0101010101010101) rc=0 (0=ACCEPTED) [des_check_key=0] inside an otherwise all-green 8916-check run (parity table, weak-key detection when enabled, schedule-vs-libcrypto). fix_run.log: patched default -> rc=-2 via the same default path.", "kernel_refs": [ "sys/crypto/des/des_setkey.c:65", "sys/crypto/des/des_setkey.c:142", "sys/crypto/des/des_setkey.c:159", "sys/crypto/des/des_setkey.c:97", "sys/crypto/des/des_setkey.c:171", "sys/netproto/smb/smb_crypt.c" ], "poc_changes": "Harness written from scratch (des_kat.c). Two environment quirks handled: (1) openssl/des.h and the kernel crypto/des/des.h share the HEADER_DES_H include guard, so the two needed libcrypto DES symbols are declared manually; (2) a memorized classic DES anchor constant (3fa40e8a984d4815) turned out to belong to a different key/plaintext pair and was dropped - the authoritative cross-check is the libcrypto agreement, which matched on all 2666 keys (the correct classic pair key=133457799BBCDFF1 pt=0123456789ABCDEF -> 85E813540F0AB405 passed as hardcoded anchor).", "attempts": 2, "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-3565 && ./build.sh", "run_cmd": "cd findings/poc/DF-3565 && ./run.sh", "code_hash": "e8c2883200dbed07e93bd06f4685ddb5e8332178dc7029a5e2a5aad4ea4b5e99", "notes": "Associated hygiene (not separately filed): des_set_key_unchecked keeps key-derived register material (c,d,s,t,t2) on the stack after return; no verbatim key copy is made. Same class as DF-3564, much weaker exposure. Guest left clean.", "recommended_fix": "Default des_check_key to 1 (fail-closed) and audit consumers for parity assumptions; see fix.diff.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "One-character default flip des_check_key=1 (des_setkey.c:65). Baseline: weak key 0101010101010101 accepted rc=0 by the default des_set_key path. Patched copy of the same source: rc=-2 (rejected via parity+weak checks); all 8915 other checks unchanged. The lone reported FAIL in the patched run is the harnesss acceptance assertion inverting, i.e. the fix taking effect.", "fix_evidence": "fix_run.log: === DF-3565 fix validation === -> des_set_key(0x0101010101010101) rc=-2 ... [des_check_key=1] contrasted with the baseline line in run.log.", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (userland same-source validation; kernel unchanged)" } |