Binwalk tutorial: how to analyse and extract firmware and hidden files step by step
Hands-on Binwalk guide: signature scans, recursive extraction, entropy, carving with dd, exploring the SquashFS rootfs and using it in CTF forensics.
This Binwalk tutorial is a hands-on, end-to-end walkthrough of firmware analysis: from the first signature scan to exploring the extracted filesystem for configuration files and credentials. You will also learn how to read entropy, how to carve data by hand with dd when automatic extraction fails, and how to apply all of it to CTF forensics challenges. If you just want to know what the tool is and how to install it, see the short Binwalk guide; here we get our hands dirty.
Legal and ethical scope: only work with firmware from devices you own, public images distributed by the vendor or project itself (for example OpenWrt, or the official update download for your router), or CTF challenge files. Analysing firmware to understand it is research; using what you find against other people's devices is not.
What a firmware image is (and why Binwalk is so useful)
The firmware of a router, IP camera or smart plug is rarely a single program. It is usually a .bin file that concatenates several pieces back to back:
- A bootloader header, very often a U-Boot uImage header, which records the architecture, size and compression of what follows.
- The Linux kernel, compressed (LZMA and gzip are the most common).
- The root filesystem (rootfs), typically SquashFS, though JFFS2, UBIFS or CramFS show up on older devices.
- Sometimes configuration partitions, digital signatures or plain
0xFFpadding.
There is no index telling you where each part begins. That is exactly what Binwalk solves: it walks the file byte by byte looking for signatures (magic numbers and known header structures) and returns a map with the offset of every component. With that map you can extract, decompress and analyse each piece separately.
Setting up the lab
The easiest setup is a virtual machine running Kali or any Debian/Ubuntu (if you are just starting, see Kali Linux for beginners). Besides Binwalk, install the utilities it relies on to extract formats:
sudo apt update
sudo apt install binwalk squashfs-tools p7zip-full cpio mtd-utils
Less common formats need extra extractors that are not always packaged: sasquatch (vendor-modified SquashFS), jefferson (JFFS2) and ubi_reader (UBI/UBIFS). We cover them in the troubleshooting section.
A note on versions: almost all documentation and tutorials out there were written for Binwalk v2 (Python). v3 is a much faster Rust rewrite that keeps the core options (-e, -M, -E, -C) but changes or drops others. This tutorial uses v2 syntax; if you have v3, check every option with binwalk --help before assuming anything.
Step 1: reconnaissance with file, strings and xxd
Before running Binwalk, spend a minute looking at the file with basic tools. They give you context and help you catch misinterpretations later.
file firmware.bin
ls -l firmware.bin
xxd firmware.bin | head -n 5
strings -n 8 firmware.bin | head -n 40
file only looks at the start of the file, so it will often just say data or recognise the u-boot header. xxd shows you the first bytes: a uImage header starts with the magic 27 05 19 56. And strings usually reveals the model name, firmware version, U-Boot or kernel version: very useful for looking up public information about the device later.
Step 2: signature scan
binwalk firmware.bin
Typical output for a MIPS-based home router looks like this (simplified, with illustrative values):
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage header, header size: 64 bytes, ... OS: Linux,
CPU: MIPS, image type: OS Kernel Image,
compression type: lzma, image name: "Linux Kernel Image"
64 0x40 LZMA compressed data, properties: 0x5D,
dictionary size: 8388608 bytes, ...
1310784 0x140040 Squashfs filesystem, little endian, version 4.0,
compression:xz, size: 2850000 bytes, 812 inodes, ...
How to read it:
- uImage header at 0x0: confirms U-Boot handles booting and tells you the architecture (MIPS, ARM...). You will need that architecture later to analyse binaries.
- LZMA at 0x40: right after the 64-byte header comes the compressed kernel.
- Squashfs at 0x140040: this is the interesting part, the filesystem holding the device's programs, scripts and configuration.
Notice how the offsets fit together: the kernel runs from 0x40 to just before the SquashFS. When pieces do not fit (signatures inside other components, impossible sizes), you are probably looking at false positives.
Step 3: automatic and recursive extraction
binwalk -e firmware.bin
The -e option extracts and decompresses everything it recognises. In v2 the result lands in _firmware.bin.extracted/, with files named after their hex offset (40, 40.7z, 140040.squashfs) and, if all goes well, an already unpacked squashfs-root/ folder.
When there are layers within layers (a tar inside a gzip that contains another image), use recursive mode:
binwalk -Me firmware.bin
-M (for matryoshka) rescans every extracted file. In v2 you can limit the depth with -d and choose the output directory with -C. Be careful: on large firmware, recursion can produce hundreds of files and eat a lot of disk space.
Since v2.3, Binwalk refuses to extract when run as root, for safety (external extractors process untrusted data). The right approach is to run it as a normal user; --run-as=root exists only for when you know what you are doing.
Step 4: entropy analysis to find compressed or encrypted data
binwalk -E firmware.bin
Entropy measures how "random" data looks, on a scale from 0 to 1. Reading it is more useful than it seems:
| Entropy pattern | What it usually means |
|---|---|
| Close to 0 | Padding (0x00 or 0xFF), empty flash regions. |
| Between 0.3 and 0.7, uneven | Uncompressed code, text, tables. |
| Flat plateau near 1 with known signatures | Compressed data (LZMA, xz, gzip). Normal. |
| Flat plateau near 1 across the whole file, no signatures | Probably encrypted or obfuscated. |
In v2, -E reports rising and falling entropy edges and produces a plot; -N skips the plot and keeps only the text. Combining it with the signature scan (binwalk -B -E firmware.bin) lets you check whether each high-entropy plateau matches a compression signature. A high-entropy region with no signature to explain it means something Binwalk does not know: a proprietary format, an encrypted partition, or data worth examining by hand.
Firmware encrypted end to end cannot be "broken" with Binwalk. The usual routes are finding an older unencrypted version, analysing the transition firmware vendors publish to migrate to the encrypted format, or, on your own device, reading the flash chip directly.
Step 5: identifying the architecture with an opcode scan
binwalk -A firmware.bin
In v2, -A searches for typical instruction patterns (such as function prologues) for common architectures like ARM, MIPS, PowerPC or x86. It is handy when there is no uImage header to tell you the CPU, for instance in microcontroller firmware or standalone blobs. If you see hundreds of "MIPSEL instructions" hits and almost none for other architectures, you know which processor to pick when loading it into a disassembler. This option is not guaranteed in v3, so check your version.
Step 6: manual carving with dd
Sometimes -e fails or extracts something corrupt. Since Binwalk already gave you the offset, you can cut the fragment yourself (carving). Using the values from the example above:
dd if=firmware.bin of=rootfs.sqsh bs=1 skip=1310784 count=2850000
file rootfs.sqsh
unsquashfs -d rootfs rootfs.sqsh
bs=1 is slow on large files. With GNU dd you can use big blocks and still specify exact bytes:
dd if=firmware.bin of=rootfs.sqsh bs=4M skip=1310784 count=2850000 iflag=skip_bytes,count_bytes
If you do not know the exact size, cut from the offset to the end of the file; most decompressors ignore trailing bytes. Then confirm with file that the header is what you expected.
Step 7: exploring the extracted filesystem
With squashfs-root/ in front of you, you effectively have the device's hard drive. This is where you really learn how it works inside. A review order that works well:
Users and passwords
cd _firmware.bin.extracted/squashfs-root
cat etc/passwd etc/shadow 2>/dev/null
Look for accounts with a password hash baked into the image: it means every device of that model shares the same factory password for that account.
Boot process and services
cat etc/inittab
ls etc/init.d/ etc/rc.d/ 2>/dev/null
grep -rn "telnetd\|dropbear\|httpd" etc/ 2>/dev/null
Startup scripts tell you which services come up: a telnetd enabled by default or a debug console are classic findings.
Hardcoded secrets
grep -rniE "passw(or)?d|secret|api[_-]?key|token" etc/ www/ usr/ 2>/dev/null | head -n 50
grep -rl "BEGIN .*PRIVATE KEY" . 2>/dev/null
find . -name "*.pem" -o -name "*.key" -o -name "*.crt"
TLS or SSH private keys shipped in the image, cloud service credentials or passwords in CGI scripts show up more often than they should. If you find this in a commercial product, the responsible thing to do is report it to the vendor (coordinated disclosure), not publish it.
Web interface and binaries
ls www/ usr/www/ 2>/dev/null
file bin/busybox usr/sbin/*
strings -n 6 bin/busybox | grep -i "busybox v"
file confirms the architecture (for example ELF 32-bit MSB executable, MIPS) and strings reveals the BusyBox or other component versions, which you can check against public vulnerabilities. The vendor's own binaries (the web server, the CGIs) are the candidates for in-depth analysis with Ghidra. To run one without the hardware, qemu-user-static lets you launch foreign-architecture binaries against the extracted root, e.g. qemu-mips-static -L . ./bin/busybox.
Binwalk in CTF forensics and steganography challenges
In CTFs the most common pattern is a file hiding another one appended to its end. The workflow:
file challenge.png
binwalk challenge.png
xxd challenge.png | tail -n 20
binwalk -e challenge.png
A PNG ends with the IEND chunk and a JPEG with the bytes FF D9. If xxd shows data after that marker, or Binwalk finds Zip archive data halfway through the file, you have your lead. If the ZIP is password-protected, go back to the image: metadata (exiftool), strings and the challenge description itself often hide the password. To decode whatever comes out (Base64, hex, XOR...), CyberChef is the Swiss army knife.
Knowing what is not suspicious saves a lot of time:
- In a PNG, Binwalk almost always reports Zlib compressed data: that is normal, the image data (IDAT chunks) is zlib-compressed.
- A JPEG with EXIF may show a second JPEG image data: usually the thumbnail embedded by the camera.
- A PDF is full of zlib streams; on its own that does not indicate anything hidden.
Binwalk only detects embedded or appended files. If the message is hidden in the least significant bits of the pixels (LSB), you will need dedicated tools such as zsteg or steghide.
Common problems and how to fix them
It detects SquashFS but does not extract it
This is problem number one. Many vendors used modified SquashFS versions (old LZMA variants, altered magic) that unsquashfs does not understand. The usual fix is sasquatch, a patched unsquashfs for those variants; once installed, Binwalk uses it automatically. Carve the fragment with dd and also try it by hand to see the actual error message.
JFFS2, UBI and other formats
If the scan shows JFFS2 filesystem, install jefferson; if it shows UBI image, use ubi_reader. Without these extractors Binwalk identifies the format but leaves the file unpacked, sometimes without a clear warning.
False positives
Some signatures are very short and appear by pure chance inside compressed data: LZMA headers with absurd sizes, certificates that are not certificates, VxWorks symbol tables in a Linux image... Be suspicious when a signature falls inside a region that already belongs to another component or when sizes make no sense. In v2 you can narrow the scan with -y (include only certain signatures) or -x (exclude them), for example binwalk -y squashfs firmware.bin.
No recognisable signatures at all
Check the entropy. If it is high and flat, it is probably encrypted (see step 4). If it is low or uneven, the file may use a proprietary header: look for strings, compare with other versions of the same firmware and try extracting from the first offset where the data makes sense.
Quick cheat sheet (v2 syntax)
| Command | Purpose |
|---|---|
binwalk fw.bin | Signature scan (map of the file) |
binwalk -e fw.bin | Extract recognised data |
binwalk -Me fw.bin | Recursive extraction |
binwalk -e -C out/ fw.bin | Choose output directory |
binwalk -E -N fw.bin | Text-only entropy, no plot |
binwalk -A fw.bin | Opcode scan to infer the architecture |
binwalk -y squashfs fw.bin | Show only signatures matching a filter |
Next steps
A good risk-free way to practise is to download the official OpenWrt image for any router, or the update your own device's vendor publishes, and repeat every step: signature map, entropy, extraction, manual dd and rootfs review. Once you are comfortable, move on to the binaries with Ghidra. And if you would rather learn forensics and file analysis through guided, progressive challenges, check out the Hackers en Acción Academy.