Files
esp32-hacking/docs/PENTEST-RESULTS.md
user 31724df63f docs: Add pentest results and update project docs
Executed non-invasive pentest against amber-maple (v1.12-dev):
- Phase 1: mDNS, port scan, binary analysis, eFuse readout
- Phase 2: HMAC timing, command injection (27 tests), replay (6 tests)
- Phase 3: NVS analysis, CVE check (12 CVEs), binary structure
All network-facing tests PASS. Physical security gaps documented.
2026-02-14 21:55:47 +01:00

7.9 KiB

Pentest Results — ESP32 CSI Sensor Firmware

Date: 2026-02-14 Target: amber-maple (192.168.129.30), ESP32-D0WD-V3 rev 3.1 Firmware: v1.11.0-11-ga4bd2a6-dirty (v1.12-dev security hardening) ESP-IDF: v5.5.2 Tester: Raspberry Pi 5 (192.168.129.11), same LAN


Results Matrix

Phase 1: Passive Reconnaissance

# Test Status Notes
1a mDNS service discovery PASS No _esp-csi._udp service advertised. Only hostname.local resolves.
1b Port scan (UDP) PASS Only 5353/udp (mDNS) and 5501/udp (cmd) open. 5500/udp closed (outbound-only).
1b Port scan (TCP) PASS All 1000 TCP ports closed. Zero TCP attack surface.
1c Firmware strings PASS No hardcoded secrets, passwords, or embedded private keys.
1c Firmware strings WARN Hardcoded default IP 192.168.129.11 and hostname amber-maple in binary.
1c Entropy analysis INFO Firmware not flash-encrypted (low entropy). Expected for dev boards.
1c PEM markers INFO mbedTLS parser constants only, not actual embedded keys.
1d eFuse: JTAG WARN JTAG_DISABLE = False — JTAG debug accessible via GPIO 12-15.
1d eFuse: Secure Boot WARN ABS_DONE_0 = False, ABS_DONE_1 = False — no secure boot.
1d eFuse: Flash Encryption WARN FLASH_CRYPT_CNT = 0 — flash encryption disabled.
1d eFuse: Download Mode WARN DISABLE_DL_ENCRYPT/DECRYPT/CACHE = False — UART download mode open.
1d eFuse: Coding Scheme INFO CODING_SCHEME = NONE — full 256-bit key space available.

Phase 2: Network Protocol Analysis

# Test Status Notes
2a Unauth STATUS info leak PASS Minimal response (hostname, uptime, rssi, channel, version). No secrets.
2a CONFIG info disclosure PASS Auth secret not exposed in unauthed CONFIG response.
2a HMAC on wire INFO HMAC tags visible in plaintext UDP (expected — no DTLS).
2a HELP disclosure INFO Command list visible without auth (by design).
2b HMAC timing oracle PASS Median diff 144us between test cases — within WiFi jitter. Constant-time comparison effective.
2c Null byte injection PASS All 17 unauthed injection payloads handled safely.
2c Format string injection PASS %x%x%n payloads rejected; no stack leak.
2c Newline/CRLF injection PASS Rejected by auth requirement.
2c Oversized packets PASS MTU-sized and 191-byte packets rejected gracefully.
2c Binary garbage PASS Random byte payloads handled without crash.
2c HOSTNAME format string PASS Authed: format strings rejected (timeout, likely sanitized).
2c HOSTNAME oversized PASS ERR HOSTNAME length 1-31 — proper validation.
2c HOSTNAME bad chars PASS ERR HOSTNAME chars: a-z 0-9 - — strict allowlist.
2c TARGET format string PASS ERR TARGET invalid IP — proper validation.
2c RATE boundary values PASS All out-of-range values rejected: ERR RATE range 10-100.
2d Valid HMAC command PASS Authed STATUS returns full response.
2d Immediate replay PASS ERR AUTH replay rejected — nonce dedup works.
2d Expired timestamp (-10s) PASS ERR AUTH expired (drift=10s) — window enforced.
2d Future timestamp (+10s) PASS ERR AUTH expired (drift=-10s) — window enforced.
2d Wrong secret PASS ERR AUTH failed — incorrect HMAC rejected.
2d Nonce cache overflow (9 cmds) PASS Replay still rejected after flooding cache with 9 unique commands.

Phase 3: Static Analysis

# Test Status Notes
3a NVS auth_secret storage FAIL Stored as plaintext string: 86bd963b07858d5b10db839d55b409df at offset 0x1ba8.
3a NVS WiFi credentials PASS No WiFi SSID/password found in NVS dump (compiled-in via sdkconfig).
3a NVS integrity WARN 2 of 6 pages have invalid CRC (uninitialized, not corruption).
3a Boot history INFO boot_count = 25 visible — leaks reboot frequency.
3b CVE exposure (12 checked) PASS 8 CVEs not applicable (unused components). 4 LOW risk (BLE scan-only mitigates).
3b CVE-2025-27840 (HCI cmds) LOW Hidden HCI commands; mitigated by scan-only BLE mode.
3b Unused #include "esp_now.h" INFO Dead include — remove to avoid link to CVE-2025-52471.
3c Stack canaries FAIL CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y — no stack overflow protection.
3c Heap poisoning WARN CONFIG_HEAP_POISONING_DISABLED=y — no heap corruption detection.
3c PMF configuration PASS CONFIG_ESP_WIFI_PMF_REQUIRED=y — 802.11w enforced.
3c Debug symbols in ELF INFO ELF has full debug info (11.9 MB). .bin for OTA is stripped.
3c cmd_task size INFO 9,877 bytes compiled — large function handling untrusted input.
3c Watchdog PASS Bootloader, interrupt, and task WDTs all enabled.

Risk Assessment

Critical (requires physical access)

Risk Impact Mitigation
No flash encryption Auth secret + WiFi creds readable from flash Enable CONFIG_FLASH_ENCRYPTION_ENABLED (irreversible eFuse)
No secure boot Arbitrary firmware flashable via UART/OTA Enable Secure Boot V2 (irreversible eFuse)
JTAG enabled Live memory inspection, breakpoints Burn JTAG_DISABLE eFuse (irreversible)
NVS plaintext auth_secret in cleartext NVS Flash encryption covers this

Medium (network-accessible)

Risk Impact Mitigation
No DTLS HMAC tokens visible to LAN sniffers Implement DTLS for command channel
No stack canaries Buffer overflow in cmd_task could be exploitable Enable CONFIG_COMPILER_STACK_CHECK_MODE_NORM
OTA over HTTP MITM firmware injection on LAN Embed CA cert, enforce HTTPS OTA

Low / Informational

Risk Impact Mitigation
Hardcoded default IP Network topology leak in binary Move to Kconfig or NVS-only default
Version string leaks git hash Aids targeted attacks Use clean tag-only version strings
HELP visible without auth Command enumeration By design — acceptable
Uptime in unauthed STATUS Aids HMAC timestamp prediction Already in minimal STATUS by design

Security Hardening Scorecard

Category Score Notes
Authentication 9/10 HMAC-SHA256, replay protection, nonce cache, rate limiting. Only gap: no mutual auth.
Input Validation 10/10 All 27 injection tests passed. Strict allowlists on HOSTNAME, RATE, TARGET, POWER.
Network Exposure 8/10 Minimal ports, PMF required, service ads removed. Gap: plaintext UDP.
Physical Security 2/10 No secure boot, no flash encryption, JTAG open, UART download mode open.
Binary Security 4/10 No stack canaries, no heap poisoning. WDTs present.
CVE Exposure 9/10 Minimal attack surface; unused components disabled; v5.5.2 patches applied.

Overall: Strong network security, weak physical security. The firmware is well-hardened against remote/network attacks. Physical access remains the primary threat vector.


Recommendations (Priority Order)

  1. P1 — Enable stack canaries: CONFIG_COMPILER_STACK_CHECK_MODE_NORM=y
  2. P1 — Enable heap poisoning: CONFIG_HEAP_POISONING_LIGHT=y
  3. P2 — Enable WDT panic: CONFIG_ESP_TASK_WDT_PANIC=y
  4. P2 — Remove unused #include "esp_now.h"
  5. P2 — Remove hardcoded default IP from binary
  6. P3 — Flash encryption (requires eFuse planning)
  7. P3 — Secure Boot V2 (requires eFuse planning)
  8. P3 — DTLS for command channel (significant effort)
  9. P3 — OTA certificate pinning