Wiki / Concepts

SquashFS

Compressed, read-only Linux filesystem. The standard way IoT firmware packs its root filesystem, and almost always what you unpack after a carve.

On-disk squashfs layout
Superblock (hsqs) 0x0
Compressed data blocks
Fragment table
Inode table
Directory table
Export / id / xattr tables
Fixed section order: the 96-byte superblock (magic 'hsqs') first, then the compressed data, then the metadata tables. binwalk finds the superblock by its magic; unsquashfs reads the tables to rebuild the tree.

SquashFS is a compressed, read-only Linux filesystem, the format most embedded devices use to pack their root filesystem into flash. When you extract a firmware image, the rootfs you unpack is almost always a squashfs.

What it is

SquashFS is designed for exactly the embedded case: pack a whole directory tree as small as possible into read-only storage, and mount it directly without unpacking. It compresses file data in blocks (GZIP, LZMA, XZ, LZO, or ZSTD depending on the build), deduplicates identical blocks, and stores its metadata (inodes, directory listings, fragment index) in separate compressed tables. The image begins with a 96-byte superblock whose magic is hsqs (little-endian sqsh), which is the signature carving tools key on.

Why it matters

The squashfs image is the device's entire userland in one file. Unpacking it gives you the binaries, web roots, startup scripts, and configuration files that routinely contain hardcoded credentials, API tokens, and private keys. Because it is read-only, its contents are exactly what ships; there is no runtime mutation to reason about. Recognising the format and its compressor is the difference between a clean extraction and a pile of garbage bytes.

How to inspect it

Most of the time binwalk carves and unpacks it in one step.

binwalk -e firmware.bin                       # carve the squashfs out of the image
cd _firmware.bin.extracted/squashfs-root/     # enter the extracted rootfs

When binwalk's bundled unpacker fails (an unusual compressor or a non-standard superblock), carve the raw filesystem and unpack it directly:

binwalk firmware.bin | grep -i squashfs       # find the superblock offset
dd if=firmware.bin of=rootfs.sqsh bs=1 skip=<offset>   # slice it out
unsquashfs -s rootfs.sqsh                      # print superblock: version + compression
unsquashfs -d out rootfs.sqsh                  # unpack the tree

unsquashfs -s reports the version and the compression algorithm, which tells you whether you need a build of squashfs-tools that supports it (older tools miss LZMA/XZ/ZSTD). Once unpacked, the wins are the usual ones: etc/shadow, embedded TLS keys and certificates, dropbear host keys, and CGI binaries under the web root.

Pitfalls

  • Vendors often use a patched or LZMA-variant squashfs that stock tools reject. If unsquashfs refuses it, try the sasquatch fork, which handles many vendor deviations.
  • The magic can appear mid-image; make sure the offset you carve from is the real superblock, not a false hit inside compressed data.
  • A truncated carve unpacks partially and silently. Compare the file count against the superblock's inode count if something looks missing.
  • Read-only means the interesting runtime state (provisioned keys, device identity) may not be in the squashfs at all, but in a separate writable partition.

Further reading