Scripts to automate the unsquash/mksquash process.
IoT & Embedded Systems Security
Track your progress through this week's content
Opening Framing
The Anatomy of a Black Box: When you hold an IoT device, you are holding a sealed
computer. The vendor does not want you to know how it works. They glue the case shut, strip the
debug headers, and encrypt the updates. Your job is to break that seal.
Why This Matters: Firmware analysis is the most efficient path to critical
vulnerabilities. While network scanning (Nmap) might show you port 80 is open, firmware analysis
shows you the PHP source code running behind port 80, the hardcoded AWS keys in `config.json`, and
the logic flaw in the authentication binary.
Real-World Relevance: The Mirai botnet succeeded because it knew the
default Telnet passwords (admin:12345) hardcoded into the firmware of 600,000 devices.
If security researchers had analyzed that firmware sooner, the vulnerability could have been patched
before the internet broke.
Week Learning Outcomes:
Identify common firmware image formats (Raw, TRX, uImage).
Extract filesystems using automated (Binwalk) and manual (dd) techniques.
Analyze entropy graphs to distinguish between code, data, and encryption.
Modify and Repack firmware to inject backdoors or enable
debugging.
Bypass basic obfuscation techniques used by vendors to hide firmware
contents.
Understanding Flash Translation Layers (FTL) and UBIFS for raw NAND
analysis.
MITRE ATT&CK for IoT Mapping:
T1052.001: Exfiltration over Physical Medium
Reading firmware directly from flash memory chips.
T1592: Gather Victim Host Information
Analyzing firmware to identify kernel versions and libraries.
T1083: File and Directory Discovery
Enumerating the filesystem for sensitive config files.
1) Firmware Acquisition: Getting the Blob
Before you can analyze firmware, you must obtain it. There are three primary paths, ranked by
difficulty.
Method A: The Vendor's Website (Easy)
Most vendors publish "updates" on their support page. This is the cleanest source—no hardware
required.
Pro: Easy to get, usually unencrypted (older devices).
Con: May only contain the "update" partition, not the full bootloader or
factory settings (NVRAM).
Method B: Man-in-the-Middle (Medium)
If the vendor only allows "Over-the-Air" (OTA) updates via the app, you capture the traffic.
Traffic Interception Workflow
# 1. Set up a Wi-Fi Access Point on your Kali machine
# 2. Connect the IoT device to your AP
# 3. Run Wireshark or tcpdump
$ sudo tcpdump -i wlan0 -w update_capture.pcap
# 4. Trigger "Check for Updates" in the mobile app
# 5. Look for HTTP GET requests or FTP transfers in the PCAP
# 6. Extract the URL: http://update.vendor.com/fw/v2.bin
Method C: Hardware Extraction (Hard)
If no update is available, you must pull it from the chip (SPI Flash) using the techniques we will
cover in Week 06 (Hardware Hacking). This gives you the "Ground Truth"—exactly what
is on the device right now, including user data (Wi-Fi passwords).
2) The Anatomy of a Firmware Image
A firmware file is not a single executable. It is a container, like a ZIP file, but often without a
standard header. It typically contains:
-A (Opcode Scan): Scan for executable code signatures (ARM/MIPS/PPC). Useful if
headers are stripped!
-E (Entropy): Visualize entropy.
--dd=".*": Extract EVERYTHING, regardless of type.
False Positives: Binwalk relies on signatures. It often finds "false" signatures in
random data.
Rule of Thumb: Check the Decimal offset. If a SquashFS header appears
at offset 1234567, is that aligned? Most partitions align to 64KB (0x10000) boundaries.
A signature at offset 1234563 is likely garbage noise.
Binwalk Workflow Diagram
[ FIRMWARE.BIN ]
|
v
[ 1. Signature Scan ] ---> Does it have Magic bytes? (e.g. 0x27051956)
|
+---> YES: [ 2. Extraction ] ---> Extract using 'dd'
| |
| +---> [ 3. Recursive Scan ] ---> Is there a file inside the file?
| (e.g. gzip inside uImage)
v
[ 4. Entropy Scan ] ---> Is it random noise? (Encryption)
4) Entropy Analysis: Seeing the Invisible
What if `binwalk` finds nothing? The firmware might be encrypted, or obfuscated.
Entropy measures the randomness of data (0 to 1).
Entropy 0.0 - 0.2: Zero padding, large blocks of same characters.
Entropy 0.5 - 0.8: English text, machine code (ARM/MIPS instructions).
Entropy 0.999...: Compressed data (Zip) OR Encrypted data (AES).
(Visualizing Entropy): A flat line at 1.0 usually means encryption. A line at 1.0 that
"dips" is often Compression (headers lower the average).
Python: Visualizing Entropy
# How Binwalk calculates entropy (simplified)
import math
import matplotlib.pyplot as plt
def shannon_entropy(data):
if not data:
return 0
entropy = 0
for x in range(256):
p_x = float(data.count(bytes([x]))) / len(data)
if p_x > 0:
entropy += - p_x * math.log(p_x, 2)
return entropy / 8.0 # Normalize to 0-1 range
def scan_file(filename, block_size=1024):
entropies = []
with open(filename, 'rb') as f:
while chunk := f.read(block_size):
entropies.append(shannon_entropy(chunk))
plt.plot(entropies)
plt.title("Firmware Entropy Analysis")
plt.show()
5) Manual Extraction (The Scalpel)
Sometimes automation fails. You need to manually carve out the chunk of file you want.
Scenario: Binwalk identifies a SquashFS starts at offset 1184292
(0x121224), but fails to extract it because the header is non-standard.
The Toolkit: dd
dd is the "Disk Destroyer" or Data Duplicator. It copies bytes from Input (if) to Output
(of).
Manual Carving Command
# Syntax: dd if=INPUT of=OUTPUT bs=1 skip=OFFSET count=LENGTH
# Extract EVERYTHING after the offset:
dd if=firmware.bin of=filesystem.squashfs bs=1 skip=1184292
# If you know the exact size (from the header size field):
dd if=firmware.bin of=filesystem.squashfs bs=1 skip=1184292 count=7384212
Correcting Headers
Vendors often corrupt standard headers (e.g., changing "hsqs" to "shsq") to break tools like
Binwalk. This is "Security by Obscurity".
Fix: Open the extracted `filesystem.squashfs` in a Hex Editor (Bless / GHex)
and change the bytes back to standard magic.
Enable Telnet: Edit squashfs-root/etc/init.d/rcS to start telnetd.
Inject binary: Copy a static gdbserver or reverse shell agent.
Repack:mksquashfs squashfs-root/ new_filesystem.squashfs -comp lzma -b 131072 Note: You MUST match the compression type (LZMA/GZIP) and block size of the
original!
Reconstruct: Append the new filesystem back to the original header+kernel.
cat header.bin kernel.bin new_filesystem.squashfs > hacked_firmware.bin
Pitfall: UID/GID Mapping When you unpack a filesystem as a regular user, all files become owned by YOU (UID 1000). When
you repack it, the files on the router will be owned by UID 1000 (which doesn't exist), not Root
(UID 0).
Fix: Always run sudo unsquashfs and sudo mksquashfs
or use the -fakeroot option to preserve permissions.
Pitfall: CRC/Checksums The device bootloader typically calculates a Checksum (CRC32) of the image before booting. If
your modified image has a different checksum (it will), the device will refuse to boot ("Brick").
Fix: You must calculate the new CRC32 and update the header (uImage header or
TRX header) using a tool like u-boot-tools.
Python Script: Calculate CRC32
import zlib
import sys
def calculate_checksum(filename):
"""Calculates CRC32 of a firmware image for header patching"""
try:
with open(filename, 'rb') as f:
data = f.read()
crc = zlib.crc32(data) & 0xFFFFFFFF
print(f"[+] File: {filename}")
print(f"[+] Size: {len(data)} bytes")
print(f"[+] CRC32: {crc:08X}")
except FileNotFoundError:
print("[-] File not found.")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python3 crc_calc.py ")
else:
calculate_checksum(sys.argv[1])
7) Defensive Architecture: Signing & Encryption
As defenders, how do we stop researchers (and attackers) from doing this?
Secure Boot (Signing)
The bootloader contains a Public Key. The firmware is signed with the Private
Key.
If verify(firmware, pub_key) == FAIL: Halt(); This prevents modification (Repacking), but not analysis.
Firmware Encryption
The entire firmware blob is encrypted (AES-CBC). The device decrypts it in RAM
during boot.
This prevents analysis (Entropy = 1.0). To defeat this, we often perform voltage
glitching to dump the decrypted RAM.
[ HW ROOT OF TRUST ] ---> [ BootROM (Immutable) ]
| Verifies (RSA-2048)
v
[ Bootloader (U-Boot) ]
| Verifies (RSA-2048)
v
[ Kernel (Linux) ]
| Verifies (Dm-Verity)
v
[ Filesystem (RootFS) ]
Case Study: The Smart Plug Fail (Simulated)
To bring this all together, let's look at a fictional but realistic engagement.
Phase 1: Discovery
We download fw_update.bin for a popular smart plug. Running binwalk
reveals a SquashFS filesystem. No encryption!
Phase 2: Extraction
We extract it: binwalk -e fw_update.bin. We find typical linux dirs: `/bin`, `/etc`,
`/usr`.
Phase 3: Analysis (The Gold Mine)
We grep for "password", "key", "token".
grep -r "aws_secret" squashfs-root/ We find a file: /etc/mqtt_config.json containing:
This password was the same for every plug. We could use this to subscribe to the
MQTT topic `#` and control ANY user's plug remotely.
Lesson: Firmware analysis turned a $20 device into a global botnet key.
Patched unsquashfs for non-standard vendor formats.
jefferson
pip3 install jefferson
Extract JFFS2 filesystems.
ubi_reader
pip3 install ubi_reader
Extract UBIFS/NAND images.
fact_extractor
(Docker)
FACT (Firmware Analysis and Comparison Tool) - Enterprise grade automation.
Security Verification Checklist
When analyzing a new firmware, run this 5-point check:
Entropy Check: Is it encrypted? (Binwalk -E)
Secrets Scan: Are there hardcoded keys? (grep / strings)
Version Check: Is the Kernel > 5 years old? (Linux 2.6 is bad)
Insecure Services: Is Telnet enabled? (RC scripts)
Permissions: Is the web server running as Root? (UID check)
Guided Lab: The Firmware Mod Kit
Objective: Manually extract a filesystem, inject a "Backdoor" file, and repack it.
Scenario: You verify a vulnerability by planting a file named pwned.txt
in the root directory.
Step 1: Analyze the Target
Download the sample file iot_camera_v1.bin (simulated).
$ binwalk iot_camera_v1.bin
128 0x80 SquashFS filesystem, little endian, version 4.0
Step 2: Extract
Since the offset is 128, let's carve it manually.
dd if=iot_camera_v1.bin of=rootfs.squashfs bs=1 skip=128
unsquashfs rootfs.squashfs
# This creates the 'squashfs-root' directory
Step 3: Modify
cd squashfs-root
echo "If you are reading this, I have root." > pwned.txt
# Verify it's there
ls -l pwned.txt
Step 4: Repack
cd ..
mksquashfs squashfs-root/ mod_rootfs.squashfs -comp gzip
# Check if size fits! IT MUST NOT BE LARGER than the flash partition.
ls -l rootfs.squashfs mod_rootfs.squashfs
Extra Credit: Header Patching
The lab isn't over. Your modified firmware is larger than the original! You cut off the header bytes
(0-128). Use cat to splice them back together.
# Get the original header (first 128 bytes)
dd if=iot_camera_v1.bin of=header.bin bs=1 count=128
# Combine
cat header.bin mod_rootfs.squashfs > pwned_firmware.bin
XP REWARD: +300 XP (Firmware Surgeon)
Outcome Check
Explain the difference between a Bootloader, Kernel, and Filesystem.
Interpret Binwalk output and identify potential false positives.
Use `dd` to manually carve binary data based on offsets.
Analyze entropy graphs to identify encrypted partitions.
Perform the Extract-Modify-Repack cycle on a Linux filesystem.
Verify CRC checksums to prevent bricking during repacking.