Firmware audit methodology
TL;DR: Firmware audit is binary audit + filesystem archaeology. Pull the image, identify the boot loader and the OS, extract the squashfs/jffs2/cramfs, walk init scripts and daemons, audit the network services and the management plane, and look for hardcoded credentials. The exploitable bugs are usually in the management web UI, the upgrade pipeline, or a custom binary daemon. Companion to firmware-extraction and uart-jtag-debug.
Phase 0 — what you have
| Acquisition | Notes |
|---|---|
| Vendor download | Easiest; vendor-distributed; sometimes encrypted |
| Dumped via SPI flash | All data, including secrets, but needs hardware |
| UART boot log | Useful for orientation; not the image itself |
| UPnP / TR-069 grab | Some devices expose firmware via update protocols |
Phase 1 — orient the image
1
2
3
4
file fw.bin
binwalk -e fw.bin # entropy + magic scan
strings -n 12 fw.bin | head -200
hexdump -C fw.bin | head -100
binwalk -e carves recognisable blobs (uImage, squashfs, JFFS2, CPIO, LZMA-compressed). The carved tree under _fw.bin.extracted/ is your filesystem.
If entropy is uniform high (≈ 7.9 bits/byte) the firmware is encrypted or compressed without a known header. Look at the boot loader (often present in clear) for the key-derivation path.
Phase 2 — identify the OS
Look inside the extracted rootfs:
1
2
3
4
ls _fw.bin.extracted/squashfs-root/
cat _fw.bin.extracted/squashfs-root/etc/os-release
cat _fw.bin.extracted/squashfs-root/etc/banner
cat _fw.bin.extracted/squashfs-root/etc/passwd
90% of consumer/SOHO devices run a Linux variant (OpenWrt, Buildroot, vendor-flavoured Linux). The rest: VxWorks, QNX, RTOS, eCos, bare-metal.
For Linux: identify init (often busybox or systemd-free), the package layout (busybox, BusyBox vs proper coreutils), and the kernel version.
Phase 3 — services and entry points
1
2
3
4
5
6
7
8
# Init scripts
grep -rn 'start\|exec' rootfs/etc/init.d rootfs/etc/rcS rootfs/usr/share/init
# inetd / xinetd
cat rootfs/etc/inetd.conf rootfs/etc/xinetd.d/*
# Systemd if present
ls rootfs/etc/systemd/system rootfs/lib/systemd/system
# Daemons referenced
strings rootfs/sbin/init | grep -iE 'web|cli|httpd|telnetd|sshd'
Build a service inventory: every daemon that listens, what port, started by whom, with what arguments.
Phase 4 — the management plane
The web UI / CLI is the highest-yield bug area. Devices ship custom code with weak inputs.
1
2
3
ls rootfs/www rootfs/etc/lighttpd rootfs/usr/share/web 2>/dev/null
find rootfs -name '*.cgi' -o -name '*.php' -o -name '*.lua' -o -name '*.asp'
file rootfs/sbin/httpd 2>/dev/null
For binary CGI handlers (mini_httpd / boa / uhttpd + a vendor binary), decompile in Ghidra (decompiler-driven-source-review) and audit:
- Argument parsing.
- Command injection via
system()/popen()with attacker-controlled strings. - Authentication: how does the device know you’re allowed?
- “Action” routing: a single endpoint dispatching by
action=parameter.
Phase 5 — credentials and secrets
1
2
3
4
strings rootfs/etc/passwd
strings rootfs/etc/shadow
grep -rn 'admin\|password\|admin12345\|root:' rootfs/etc rootfs/usr/share
find rootfs -name '*.conf' -exec grep -l 'password' {} \;
Hardcoded admin/admin still ships in 2026. Check:
- Recovery accounts in
/etc/passwd. - Backdoor passwords compared in binaries (
strings | grep -i magic). - Pre-shared keys in config files.
- TLS certificates with private keys bundled.
Phase 6 — upgrade pipeline
The firmware upgrade endpoint is itself an attack surface:
- Image signature verified? (look for
openssl dgst,RSA_verify,ed25519). - Image encrypted in transit? In storage?
- Atomic apply, or write-as-you-go?
- “Recovery” image that’s signed weaker than the main image?
A weak upgrade verification is the path to permanent device takeover.
Phase 7 — kernel modules and drivers
1
2
find rootfs -name '*.ko'
modinfo *.ko 2>/dev/null | head
Custom drivers are often the buggiest part of the stack. IOCTL handlers (see windows-driver-ioctl-audit for the analogous Windows pattern, kernel-syscall-source-review for Linux kernel review) frequently take attacker-controlled lengths into copy_from_user calls without bounds.
Phase 8 — secondary processors
Some devices have an applications processor (Linux) and a secondary microcontroller for radio, BMC, etc. The secondary often runs RTOS code with even weaker security. Look for separate firmware blobs (*.fw files in /lib/firmware/).
Common bug patterns by class
- Routers: WAN-side command injection via DDNS, UPnP, TR-069.
- Cameras: ONVIF/RTSP handlers with stack BOFs; default creds.
- IP phones: SIP parsing bugs; provisioning endpoints with hardcoded URLs.
- Printers: PJL command injection; SNMP info disclosure.
- Industrial gateways: Modbus / DNP3 unauth control; OPC-UA cert misuse.
- Consumer IoT: MQTT brokers exposed with default creds; OTA update without signing.
Static + dynamic loop
Static analysis points at a function; dynamic confirms.
For Linux-userland binaries, use qemu-user to run binaries on your Kali:
1
2
3
4
sudo apt install qemu-user-static
cp $(which qemu-mipsel-static) rootfs/usr/bin/
chroot rootfs /sbin/httpd -p 8080
# now you can fuzz/repro from outside, with the device's daemon running on your host
For full-system emulation, see Firmadyne, FirmAE, and Avatar2.
Reporting
For a vendor report you want:
- Acquired firmware version (filename + sha256).
- Affected component (binary path inside the image).
- Reproducer (Python script or curl one-liner) that triggers the bug from network.
- Pre-auth vs post-auth.
- Suggested fix.