CtrlK
BlogDocsLog inGet started
Tessl Logo

binwalk-extract

Firmware image extraction with binwalk and firmware-mod-kit — recursive archive carving, squashfs/jffs2/ubifs mounting, entropy analysis to detect packed/encrypted regions, and nested container handling. Entry point for all static filesystem analysis after a raw binary image is acquired.

76

Quality

96%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Medium

Suggest reviewing before use

SKILL.md
Quality
Evals
Security

Firmware Extraction with binwalk

Turn a raw binary image into a navigable filesystem tree. binwalk handles most common containers; firmware-mod-kit covers special-case squashfs variants; manual mounting handles the rest.

Prerequisites

  • Raw firmware binary (from firmware-acquisition skill).
  • Tools: binwalk (≥ 2.3), sasquatch (non-standard squashfs decompressor), jefferson (JFFS2 extractor), ubireader (UBI/UBIFS), mtd-utils, firmware-mod-kit (FMK), 7-zip, lzma, xz-utils.
# Kali / Debian install
sudo apt-get install -y binwalk firmware-mod-kit mtd-utils
pip3 install jefferson ubireader

# sasquatch (handles non-standard squashfs compression: LZMA, XZ, ZLIB with vendor patches)
git clone https://github.com/devttys0/sasquatch && cd sasquatch
./build.sh && sudo cp sasquatch /usr/local/bin/

Step 1 — Identify + Entropy Survey

FW=/workspace/evidence/iot/<target>/firmware/flash_full.bin

# Quick filetype + offset scan
binwalk "$FW"

# Entropy analysis: flat line near 1.0 = encrypted/compressed; structured = filesystem
binwalk -E "$FW"
# Output plot to PNG for report
binwalk -E --save "$FW"     # saves <fw>.png beside the binary

# High entropy region with no signature = encrypted blob — note offset + size
# Low-to-medium entropy with known FS signature = squashfs/jffs2/cramfs — extract

Reading the entropy graph

Entropy rangeInterpretation
0.0–0.3Mostly zero-fill / padding — skip
0.5–0.8Structured data (FS headers, ELF) — extract
0.8–0.95Compressed data (gzip, lzma, zlib) — normal
0.95–1.0 flatAES/RSA encrypted or already-compressed blob — flag for key hunt

Step 2 — Recursive Extraction

OUTDIR=/workspace/evidence/iot/<target>/extracted

# Recursive extraction (-M), follow symlinks (-r), output to dedicated dir (-C)
binwalk -eM -C "$OUTDIR" "$FW"

# Inspect what was carved
find "$OUTDIR" -maxdepth 4 -type f | head -60
ls -lah "$OUTDIR"/_*

Common extraction outputs

_flash_full.bin.extracted/
  squashfs-root/          ← mounted squashfs rootfs
  40          ← raw uImage kernel (strip 64-byte header for vmlinuz)
  40.7z       ← carved archive at offset 0x40
  A00000      ← raw block at offset 0xA00000

Step 3 — Squashfs (standard + vendor variants)

SQFS=$(find "$OUTDIR" -name "*.squashfs" -o -name "squashfs-root.img" 2>/dev/null | head -1)

# Standard unsquashfs
unsquashfs -d /tmp/squashfs_root "$SQFS"

# Non-standard (Broadcom LZMA, TP-Link XZ, vendor-patched):
sasquatch -d /tmp/squashfs_root "$SQFS"

# If both fail, force a specific compression type:
sasquatch -p 1 -le -d /tmp/squashfs_root "$SQFS"   # little-endian
sasquatch -p 1 -be -d /tmp/squashfs_root "$SQFS"   # big-endian

# Verify extraction
ls /tmp/squashfs_root/{bin,etc,lib,usr,var} 2>/dev/null

Step 4 — JFFS2

JFFS2_IMG=$(find "$OUTDIR" -name "*.jffs2" 2>/dev/null | head -1)

# Method A: jefferson (Python, handles most variants)
jefferson "$JFFS2_IMG" -d /tmp/jffs2_root

# Method B: kernel loop mount (requires modprobe jffs2 + mtdram)
sudo modprobe mtdram total_size=65536 erase_size=256
sudo modprobe mtdblock
sudo dd if="$JFFS2_IMG" of=/dev/mtd0
sudo mount -t jffs2 /dev/mtdblock0 /mnt/jffs2

Step 5 — UBIFS (NAND-based devices)

UBI_IMG=$(find "$OUTDIR" -name "*.ubi" -o -name "*.ubifs" 2>/dev/null | head -1)

# ubireader_extract_files: most direct path
ubireader_extract_files -o /tmp/ubifs_root "$UBI_IMG"

# For raw UBI volume images, ubiextract:
sudo modprobe ubi
sudo ubiattach -m 0 -d 0 /dev/ubi_ctrl
sudo mount -t ubifs /dev/ubi0_0 /mnt/ubifs

Step 6 — Manual Carving (when binwalk misses)

# Find filesystem magic bytes manually
python3 -c "
import sys
data = open('$FW','rb').read()
sigs = {b'hsqs': 'SquashFS LE', b'sqsh': 'SquashFS BE',
        b'\\x19\\x85': 'JFFS2', b'UBI#': 'UBI', b'\\x27\\x05\\x19\\x56': 'uImage'}
for sig, name in sigs.items():
    off = 0
    while True:
        idx = data.find(sig, off)
        if idx == -1: break
        print(f'  {name} @ 0x{idx:08x}')
        off = idx + 1"

# Carve a specific region for separate analysis
dd if="$FW" bs=1 skip=$((0xA00000)) count=$((0x600000)) of=/tmp/carved_rootfs.bin

# Run binwalk on the carved piece
binwalk -eM -C /tmp/carved_extract /tmp/carved_rootfs.bin

Step 7 — Nested Archive Handling

# Device firmware often wraps: .zip/.tar → signed header → lzma → squashfs
# firmware-mod-kit handles multi-layer TP-Link / Netgear / Asus formats:
cd /opt/firmware-mod-kit
./extract-firmware.sh "$FW"
ls /tmp/fmk/

# D-Link WRGG (proprietary container):
binwalk --dd='.*' "$FW"     # dump ALL matched signatures
file _*.extracted/*

# Lzma-raw regions binwalk missed (entropy ~0.9, no gzip magic):
lzma -d < /tmp/region.lzma > /tmp/region.decompressed
xz -d < /tmp/region.xz > /tmp/region.decompressed

Step 8 — Post-Extraction Triage

ROOT=/tmp/squashfs_root   # adjust to wherever rootfs landed

# Architecture + OS identification
file "$ROOT/bin/busybox"
readelf -h "$ROOT/bin/busybox" | grep -E "Machine|Class|Data"

# Enumerate interesting paths
ls "$ROOT/etc/"
ls "$ROOT/usr/bin/" | head -40
find "$ROOT" -name "*.conf" -o -name "*.ini" -o -name "*.cfg" | head -30

# SUID / SGID binaries (potential priv-esc on device)
find "$ROOT" -perm -u=s -type f 2>/dev/null
find "$ROOT" -perm -g=s -type f 2>/dev/null

# World-writable directories (writeable by web/telnet processes)
find "$ROOT" -perm -o=w -type d 2>/dev/null | grep -v proc

# Symlinks that escape the rootfs (path traversal potential)
find "$ROOT" -type l | while read l; do
    target=$(readlink "$l")
    echo "$l -> $target"
done | grep '^\.\.'

Evidence

EVDIR=/workspace/evidence/iot/<target>/extracted
mkdir -p "$EVDIR"
# Save extraction tree summary
find /tmp/squashfs_root -type f > "$EVDIR/file_tree.txt"
# Save entropy plot
cp "$FW.png" "$EVDIR/entropy_plot.png" 2>/dev/null || true
# Note encrypted regions for follow-up
echo "Encrypted blob @ 0xXXXXXX, length 0xYYY — likely AES-CBC, key TBD" \
    >> "$EVDIR/notes.txt"

OPSEC Notes

  • binwalk -eM can write gigabytes if the firmware contains recursive containers; run on a dedicated partition or tmpfs with sufficient space.
  • Some vendor firmware images are signed; extraction still works (signature is just a header — binwalk skips it). Repacking for re-flash requires bypassing signature verification (see bootloader-uboot skill).
  • Encrypted regions at entropy ≈ 1.0 with no detectable IV/tag structure may indicate XTS-AES or ChaCha20 stream — note the offset; search the OTA update binary or companion app for the key material.

References

  • binwalk docs: https://github.com/ReFirmLabs/binwalk/wiki
  • sasquatch: https://github.com/devttys0/sasquatch
  • jefferson (JFFS2): https://github.com/sviehb/jefferson
  • ubireader: https://github.com/jrspruitt/ubi_reader
  • firmware-mod-kit: https://github.com/rampageX/firmware-mod-kit
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.