/FIRMWARE · 2026-08-12
Unsigned updates are the incident waiting to happen
If the box will install a tarball over HTTP because a checkbox in the UI said so, the rest of the binary review is commentary. Say that first.
We still get images where the updater is wget plus tar -x plus reboot. Sometimes there is an HMAC with the key in the same filesystem. Sometimes there is a signature check that the debug build compiled out. It is the finding CISOs understand.
We describe the path in the image. We do not publish a recipe for pushing a malicious image at a stranger’s device.
What “signed” has to mean
- A public key in ROM or in a write-protected partition — not only in squashfs next to the updater.
- A check that runs before flash, not a log line after.
- A failure that refuses to boot the new image, not a warning in dmesg.
- A documented way to rotate the key. If there is none, write that.
HMAC with a shipped key
That is a checksum with extra steps. Anyone who unpacked the image has the key. Report it as “integrity, not authenticity.” Do not call it secure boot.
HTTPS to a vendor CDN
Transport is not image signing. A stolen CDN path still installs. If the only control is TLS to a hostname, say “transport only.”
Secure boot theater
Verified boot on the kernel and an unsigned overlay for “app.” The overlay is the product. We have seen this. The report names which hop is verified. See the boot chain.
Where we look
grep -RInE 'fw_upgrade|sysupgrade|do_upgrade|mtd write' squashfs-root
# then Ghidra on that binary: does it call a verify() before flash?
# strings: BEGIN PUBLIC KEY, HMAC, openssl
If we cannot find a verify, we do not invent one. “No signature check found in the updater” is a complete sentence. Confirm with the vendor if they claim otherwise — they sometimes have a second updater not in this SKU.
What we do not do
We do not build a replacement image for the public internet. In a lab named in the letter, we may install a benign marker image to show the check is absent. That demonstration stays in the report. It is not a module.