Tutoriels

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.

Publié le 3 novembre 202611 min de lecture0 views

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 0xFF padding.

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 patternWhat it usually means
Close to 0Padding (0x00 or 0xFF), empty flash regions.
Between 0.3 and 0.7, unevenUncompressed code, text, tables.
Flat plateau near 1 with known signaturesCompressed data (LZMA, xz, gzip). Normal.
Flat plateau near 1 across the whole file, no signaturesProbably 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)

CommandPurpose
binwalk fw.binSignature scan (map of the file)
binwalk -e fw.binExtract recognised data
binwalk -Me fw.binRecursive extraction
binwalk -e -C out/ fw.binChoose output directory
binwalk -E -N fw.binText-only entropy, no plot
binwalk -A fw.binOpcode scan to infer the architecture
binwalk -y squashfs fw.binShow 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.

Guide gratuitApprenez le hacking éthique de zéro

Tags

binwalkfirmwareforensicsctfiot
PartagerTwitterLinkedIn

// ARTICLES_CONNEXES