Firmware Analysis Notes
These are my notes for firmware analysis. I will update them overtime as I learn more.
Basic questions to ask
- What is the target device used for?
- What is the CPU architecture?
- What features does the device support?
- What type of operating system runs on the device?
- How do users interact with the device? (i.e web app, mobile app)
- What other chips are onboard?
- What type of software/programs/services run on it?
- Did you read the release notes?
- Where is the firmware available from? (vendor site, extracted from device, MITM update capture)
- Has this firmware version been analyzed before? Check Exploit-DB and GitHub.
- What encryption or compression is applied to the firmware?
- Are there known CVEs for this device or kernel version?
- Are default credentials documented? (FCC filings, vendor manuals, online databases)
- Does the device expose a UART or JTAG debug interface?
- What is the bootloader? (U-Boot is common in embedded Linux)
- What filesystem type is used? (SquashFS, JFFS2, YAFFS2, CramFS)
- Does the firmware verify its own integrity or use secure boot?
[!NOTE] The commands below assume a Linux host. Most tools (
binwalk,binutils,jefferson,checksec) are pre-installed on Kali Linux. For other distros, install them via your package manager or from source.
Commands and Tools
Initial Identification
Before extracting anything, profile the firmware blob itself.
// identify file type and magic bytes
file firmware.bin
// print human-readable strings (minimum 8 chars to reduce noise)
strings -n 8 firmware.bin | less
// hex dump — inspect the first 512 bytes for headers
xxd firmware.bin | head -32
// alternatively with hexdump
hexdump -C firmware.bin | head -32
// check entropy (high entropy = compressed or encrypted regions)
binwalk -E firmware.bin
// find interesting strings: URLs, IPs, credentials, keys
strings -n 8 firmware.bin | grep -Ei "http|ftp|password|passwd|admin|root|ssh|key|token"
strings -n 8 firmware.bin | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
Extraction
binwalk is the primary tool for scanning and carving firmware images.
// scan file for signatures
binwalk firmware.bin
// scan for common file signatures with terminal formatting
binwalk --signature --term firmware.bin
// extract all recognized formats
binwalk -e firmware.bin
// recursively extract nested archives
binwalk -Me firmware.bin
// extract a raw region manually with dd (use decimal offset from binwalk output)
dd if=firmware.bin of=squashfs.bin bs=1 skip=<DECIMAL_OFFSET> count=<SIZE>
// extract SquashFS manually if binwalk fails
unsquashfs squashfs.bin
// extract JFFS2 flash filesystem images
jefferson jffs2.bin -d output_dir
// decompress a gzip blob
gunzip -c compressed.gz > decompressed.bin
[!NOTE] SquashFS is the most common filesystem in consumer router firmware. If
binwalk -eproduces a_firmware.extracteddirectory containing asquashfs-rootfolder, that is your extracted filesystem root.
Filesystem Exploration
After extraction, treat the filesystem root like a live Linux system. The goal is situational awareness: what is running, who can log in, and what credentials are baked in.
[!WARNING] Never run extracted firmware binaries directly on your host machine. Use a VM or an emulator like QEMU.
Orientation
// determine Linux kernel version from kernel modules
find . -name "*.ko" | head -5
strings ./lib/modules/*/*.ko 2>/dev/null | grep "vermagic"
// identify the init system
cat etc/inittab
ls etc/init.d/
ls etc/rc.d/ 2>/dev/null
// find the busybox binary (common in embedded Linux)
find . -name "busybox" 2>/dev/null
file ./bin/busybox
// list all installed binaries
ls -la bin/ sbin/ usr/bin/ usr/sbin/
Credentials
// user accounts and password hashes
cat etc/passwd
cat etc/shadow
// search for hardcoded passwords in config files
grep -irl "password" . --include="*.conf" --include="*.cfg" --include="*.xml" --include="*.json"
grep -ir "passwd\|password\|pass=" . --include="*.sh" 2>/dev/null
// search for private keys
grep -irl "BEGIN RSA PRIVATE KEY" .
grep -irl "BEGIN EC PRIVATE KEY" .
grep -irl "BEGIN OPENSSH PRIVATE KEY" .
find . -name "*.pem" -o -name "*.key" -o -name "*.crt" 2>/dev/null
// find .htpasswd files (web basic auth credentials)
find . -name ".htpasswd" 2>/dev/null
Network and Services
// web server config
ls -la etc/nginx/ etc/apache2/ etc/lighttpd/ etc/httpd/ 2>/dev/null
// web root
ls -la var/www/ srv/www/ 2>/dev/null
// look for CGI scripts (common attack surface in embedded web UIs)
find . -name "*.cgi" 2>/dev/null
// check for listening services configured via inetd/xinetd
cat etc/inetd.conf 2>/dev/null
cat etc/xinetd.conf 2>/dev/null
ls etc/xinetd.d/ 2>/dev/null
// SSH server configuration
cat etc/ssh/sshd_config 2>/dev/null
// find hardcoded IPs and URLs
grep -Ero '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' . 2>/dev/null | sort -u
grep -Erio 'https?://[a-zA-Z0-9./?=_%:-]+' . 2>/dev/null | sort -u
Startup and Persistence
// startup scripts
cat etc/inittab
ls -la etc/init.d/
cat etc/rc.local 2>/dev/null
// cron jobs
cat etc/crontab 2>/dev/null
ls etc/cron.d/ etc/cron.daily/ 2>/dev/null
// users and home directories
ls -latr home/
cat etc/group
// scripts in common locations
ls -l usr/local/bin
ls -l bin
Binary Analysis
Once you have the extracted filesystem, analyze individual binaries for security weaknesses.
File identification
// identify architecture and ELF type
file bin/busybox
file usr/sbin/httpd
// read ELF header (shows CPU architecture, endianness, entry point)
readelf -h usr/sbin/httpd
// display all sections
readelf -S usr/sbin/httpd
// list dynamic dependencies and imported symbols
readelf -d usr/sbin/httpd
readelf --syms usr/sbin/httpd
Security mitigations
// check for NX, RELRO, stack canaries, PIE
checksec --file=usr/sbin/httpd
// audit all ELF binaries at once
find . -type f -exec file {} \; 2>/dev/null | grep ELF | cut -d: -f1 | xargs -I{} checksec --file={}
// rabin2 equivalent (radare2 toolkit)
rabin2 -I usr/sbin/httpd
// check for RPATH issues (potential library hijacking)
rabin2 -l usr/sbin/httpd
Strings and disassembly
// dump all strings from a binary
strings -n 8 usr/sbin/httpd
// rabin2 strings with section context
rabin2 -z usr/sbin/httpd
rabin2 -zzz usr/sbin/httpd
// disassemble with objdump
objdump -d usr/sbin/httpd | less
objdump -D -M intel usr/sbin/httpd | less // x86 only
// list symbol table
objdump -tT usr/sbin/httpd
nm -D usr/sbin/httpd 2>/dev/null
Dangerous function hunting
// scan all ELF binaries for dangerous C functions
find . -type f | xargs file 2>/dev/null | grep ELF | cut -d: -f1 | while read bin; do
result=$(nm -D "$bin" 2>/dev/null | grep -E "strcpy|strcat|sprintf|gets|system|popen|exec")
[ -n "$result" ] && echo "=== $bin ===" && echo "$result"
done
Tool Summary
| Tool | Purpose | Common flags |
|---|---|---|
file | Identify file type and architecture | — |
strings | Extract readable strings from a binary | -n 8 (min length) |
xxd / hexdump | Hex inspection | xxd -l 256 for first 256 bytes |
binwalk | Signature scan, entropy analysis, extraction | -Me recursive extract, -E entropy |
dd | Raw binary carving by offset | bs=1 skip=<offset> count=<size> |
unsquashfs | Extract SquashFS images | — |
jefferson | Extract JFFS2 images | -d <output_dir> |
readelf | ELF header, section, and symbol analysis | -h, -S, -d, --syms |
objdump | Disassembly and symbol tables | -d, -D, -tT |
checksec | Security mitigation audit | --file=<binary> |
rabin2 | Multi-purpose binary info (radare2) | -I, -z, -l, -i |
nm | Symbol listing | -D for dynamic symbols |
grep | Pattern search across filesystem | -irl recursive, case-insensitive |
Worked Example: Netgear WNAP320
Below is an example of extracting vmlinux.bin from the Netgear WNAP320 firmware.
$ binwalk vmlinux.gz.uImage
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, header CRC: 0x8B048C59, created: 2011-06-23 10:45:30, image size: 983040 bytes, Data Address: 0x80020000, Entry Point: 0x801F2000, data CRC: 0xC0C376EF, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: gzip, image name: "Linux Kernel"
64 0x40 gzip compressed data, has original file name: "vmlinux.bin", from Unix, last modified: 2011-06-23 10:45:30
The uImage header states that the firmware is Linux MIPS. If we want to get the exact version of the Linux Kernel, we will need to extract the gzip compressed vmlinux.bin data.
It is stored at offset 64, so use dd to extract it:
// save output to vmlinux.bin.gz
$ dd if=vmlinux.gz.uImage of=vmlinux.bin.gz bs=1 skip=64
$ gunzip vmlinux.bin.gz
Find the Linux Kernel version and GCC version
$ strings vmlinux.bin | grep "Linux version"
Linux version 2.6.23-WNAP320_V2.0.3 (root@build) (gcc version 4.2.4) #1 Thu Jun 23 16:06:18 IST 2011
Despite this Netgear Wireless Access Point being released in November 2015, it is using very old versions of the Linux kernel and gcc compiler. Linux kernel version 2.6.23 released in October 2007 and gcc 4.2.4 released in May 2008!
Further Resources
- Intro to Firmware Analysis (PancakesCon2020)
- Reverse engineering my router’s firmware with binwalk
- Firmware Analysis Toolkit (FAT)
- EMBA — Embedded Linux Analyzer
- Firmwalker — firmware filesystem scanner
- OpenWrt CVE database
- IoT Inspector (FCC filings and hardware info)
- Practical IoT Hacking — Fotios Chantzis et al. (No Starch Press)