DF-4208 / verdict.json
{ "finding_id": "DF-4208", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "Reproduced via source-faithful harness on the audit guest: legacyEepromDiag() (verbatim kernel code, sha256-gated extraction) on a 2413-layout (eepMap==2, AR2413/AR5413) card returns *result = 0x167114c81478143c \u2014 byte-for-byte the four EEPROM-derived pier frequencies pChannels[0..3] reinterpreted as a pointer (provenance check: match YES) \u2014 which is non-canonical, so the SIOCGATHDIAG copyout in ath_ioctl_diag() page-faults -> kernel panic; with 5 piers pe->numChannels aliases pChannels[4] giving *resultsize = 498,800 EEPROM-derived bytes. HAL_DIAG_EEPROM additionally dumps all 6568 bytes of HAL_EEPROM including six live kernel heap pointers. Reachability is conditional: SIOCGATHDIAG has no privilege check in DragonFly (if_ath_ioctl.c:299) but ATH_DIAGAPI is not in X86_64_GENERIC, so the unprivileged panic/pointer-leak requires a custom kernel with options ATH_DIAGAPI plus an ath NIC \u2014 hence on-guest impact none (guest has no ath hardware and no ATH_DIAGAPI). fix.diff (eepMap gate) validated: EXP codes refused, NULL result, exit 0.", "exploit_chain": "custom kernel with options ATH_DIAGAPI + AR2413/AR5413 NIC + unprivileged local user -> SIOCGATHDIAG(HAL_DIAG_EEPROM_EXP_11A) -> legacyEepromDiag union confusion -> copyout from non-canonical pointer -> kernel panic (DoS); secondary: HAL_DIAG_EEPROM leaks 6 kernel heap pointers (KASLR/heap-layout); a uid0 chain is not available from this primitive (fault is deterministic, no controlled write)", "evidence": "run.log [EXP_11A] *result = 0x167114c81478143c + NON-CANONICAL kernel pointer ... PANIC + provenance pointer == pChannels[0..3] as u64 ... (match: YES) + 5-pier variant ... *resultsize = 498800 bytes; fixed section shows returned 0, NULL result, exit 0", "kernel_refs": [ "sys/dev/netif/ath/ath_hal/ah_eeprom_v3.c:1717", "sys/dev/netif/ath/ath_hal/ah_eeprom_v3.c:1729", "sys/dev/netif/ath/ath_hal/ah_eeprom_v3.c:1732", "sys/dev/netif/ath/ath_hal/ah_eeprom_v3.h:447", "sys/dev/netif/ath/ath/if_ath_ioctl.c:214", "sys/dev/netif/ath/ath/if_ath_ioctl.c:299", "sys/dev/netif/ath/ath_hal/ar5212/ar5212_misc.c:1061" ], "poc_changes": "No working seed; built sha256-gated verbatim extraction of the full HAL_EEPROM type + legacyEepromDiag from the guest /usr/src; RD_EDGES_POWER had to be extracted first (HAL_EEPROM references it); the AH_PRIVATE shim declared after the struct block; diag constants taken from ah_diagcodes.h values; emulated copyout canonicity check instead of real ioctl (no ath hardware in guest).", "attempts": 4, "guest_uname": "DragonFly dfbsd 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": 8, "guest_dirty": 0, "build_cmd": "cd findings/poc/DF-4208 && ./build.sh", "run_cmd": "cd findings/poc/DF-4208 && ./run.sh", "code_hash": "5283c4e818fd48e27359bc190caaba407c793f5b4bf53c6e6d1b6855a351b622", "notes": "Conditional reachability: ATH_DIAGAPI kernels only (LINT64 yes, X86_64_GENERIC no) + ath card; unprivileged once compiled in (no priv_check on SIOCGATHDIAG in DragonFly, unlike SIOCZATHSTATS). Recommend also adding the missing privilege check to ath_ioctl_diag and redacting ee_u pointers from the HAL_DIAG_EEPROM dump.", "recommended_fix": "Gate HAL_DIAG_EEPROM_EXP_* in legacyEepromDiag on the 5112 layout actually being live (ee_version >= AR_EEPROM_VER4_0 && ee_eepMap == 1); independently add a privilege check to ath_ioctl_diag.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (eepMap/version gate) applied to the verbatim extraction: legacyEepromDiag returns AH_FALSE for EXP codes on eepMap==2, *result stays NULL, copyout skipped, exit 0 \u2014 non-canonical pointer eliminated. git apply --check clean against sys/; never applied to the read-only tree.", "fix_evidence": "run.log ===== with fix.diff applied ===== section: legacyEepromDiag(EXP_11A) returned 0, *result = NULL ... copyout skipped, PASS (fixed) ... exit=0, RESULT: union confusion reproduced on stock, GONE with fix" } |