Schedule
- Schedule
- How to read this page
- Schedule at a glance (SUBJECT TO CHANGE – CHECK REGULARLY)
- Week 1 — Introduction and hardware fundamentals
- Week 2 — UART and instrumentation
- Week 3 — I2C and SPI
- Week 4 — JTAG and SWD
- Week 5 — Firmware extraction from memory
- Week 6 — Networks, WiFi, BLE, and firmware from the wire
- Week 7 — RF capture and spectrum analysis
- Week 8 — Radio hacking: LoRa, Zigbee, Thread, and Matter
- Week 9 — Software reverse engineering and bootloaders
- Week 10 — IoT operating systems and secure architecture
- Assignments at a glance
- Key takeaways
- References
How to read this page
This is the ten-week plan for CS 410/510: IoT Security. It expands the Major Topics list on the course home page into a week-by-week sequence, with the reading, the bench work, and the reasoning behind the ordering.
Dates below are for Fall 2026: instruction runs 28 September – 6 December 2026, and finals week is 7–11 December. The university is closed on Wednesday 11 November (Veterans Day), so Week 7 has a single meeting. Every assignment is due at 23:59:59 on the Friday listed.
Each week also has three short in-class exercises — a five-minute warm-up reviewing the previous week and two 10–15 minute hands-on blocks — collected on the Classroom Activities page. They rehearse the week’s skill on a different artifact than the homework uses, so doing them does not solve your assignment for you.
The sequence is deliberate, and it is the same sequence a real assessment follows: you cannot analyze what you cannot capture, you cannot capture what you cannot probe, and you cannot probe what you cannot identify. Weeks 1–4 build the ability to get a signal off a board. Weeks 5–6 turn signals into firmware. Weeks 7–8 do the same job over the air instead of over a wire. Weeks 9–10 turn firmware into understanding, and then ask what a defensible device would have looked like in the first place.
⚠️ Scope and authorization. Every technique in this course is applied to hardware you own or hardware provided by the course. Attaching a logic analyzer to someone else’s device, capturing someone else’s radio traffic with intent to decode it, or extracting firmware from a device you do not own ranges from a licence-agreement violation to a federal crime depending on the device and the jurisdiction. When in doubt, ask before you probe. The lab notebook discipline in Week 1 exists partly so you can demonstrate what you did and why.
Schedule at a glance (SUBJECT TO CHANGE – CHECK REGULARLY)
| Week | Meetings (MW) | Topic | Assignment | Due (Fri, 23:59:59) |
|---|---|---|---|---|
| 1 | Sep 28, Sep 30 | Course intro; IoT threat models; the lab notebook Introduction to hardware: chip identification, pin identification, datasheets Soldering and bench safety |
HW #1 | Lab 0 — Oct 2 |
| 2 | Oct 5, Oct 7 | UART: the bootloader console Logic analyzers and instrumentation Signal integrity, voltage domains, and level shifting |
Lab 1, HW #1 — Oct 9 | |
| 3 | Oct 12, Oct 14 | I2C: addressing, ACK/NAK, bus sniffing SPI: the four-wire bus and why flash lives here Bus contention and passive tapping |
HW #2 | |
| 4 | Oct 19, Oct 21 | JTAG and SWD: on-chip debug Boundary scan, pin discovery, and the JTAGulator Debug port lockdown and how it fails |
Lab 2, HW #2 — Oct 23 | |
| 5 | Oct 26, Oct 28 | Firmware extraction from on-device memory SPI-NOR flash dumping; in-circuit vs. chip-off binwalk, entropy analysis, filesystem carving |
HW #3 | Final project proposal — Oct 30 |
| 6 | Nov 2, Nov 4 | Network hacking: a brief overview WiFi and Bluetooth/BLE in the IoT context Firmware extraction from packet captures (OTA interception) |
HW #3 — Nov 6 | |
| 7 | Nov 9 only (Nov 11 — Veterans Day, university closed) |
RF capture and spectrum analysis SDR fundamentals: sampling, IQ, waterfalls Modulation identification |
HW #4 | Lab 3 — Nov 13 |
| 8 | Nov 16, Nov 18 | Radio hacking: LoRa and LoRaWAN Zigbee, Thread, and Matter Replay, frame counters, and key management |
HW #4 — Nov 20 | |
| 9 | Nov 23, Nov 25 | Software reverse engineering: Ghidra on embedded firmware Cross-architecture RE (ARM, Xtensa, RISC-V) Bootloaders and the chain of trust |
HW #5 | |
| 10 | Nov 30, Dec 2 | IoT operating systems: OpenWRT, FreeRTOS, VxWorks, Mbed, Windows IoT Secure architectures and certification baselines Project work and wrap-up |
HW #5 — Dec 4 | |
| Finals | Dec 7–11 no class meeting |
No class meeting | Final Project — Dec 11 |
Week 1 — Introduction and hardware fundamentals
Objectives
- Describe the IoT attack surface in terms of a concrete threat model.
- Identify an unknown IC from its package markings.
- Read a datasheet well enough to find a pinout, an operating voltage, and a communications interface.
- Keep a lab notebook that would survive a legal challenge.
Original material: the four questions of hardware recon
Every hardware assessment starts in the same place — a board you have never seen, covered in parts you cannot name. Work the following four questions in order, because each one narrows the next:
- What is the biggest chip? The part with the most pins is usually the main processor or SoC. Its markings tell you the architecture, which tells you what disassembler settings you will need in Week 9.
- What is next to it? A small 8-pin part beside the SoC is almost always SPI-NOR flash holding the firmware. That single observation drives all of Week 5. An 8-pin part with a crystal nearby may be an RTC on I2C instead.
- What connects to the outside world? Radios, Ethernet PHYs, and USB controllers define the remote attack surface. Antenna traces are visible to the naked eye — look for the meandering copper.
- What is left over? Unpopulated headers, test pads, and rows of vias are the manufacturer’s own debug access. They are left over because removing them costs money and nobody budgeted for it. These become Weeks 2 and 4.
The discipline here matters more than the speed. Photograph the board before you touch it, at high resolution, from both sides. You will want that photo when you have desoldered something and cannot remember which way round it went.
Original material: package markings and what they hide
Markings are not standardized, but they are conventional. A typical line reads
as manufacturer logo, part number, then a date/lot code. The date code is
usually YYWW — 2417 is the seventeenth week of 2024. That gives you a
lower bound on the firmware’s age, which is a useful cross-check when a
vendor claims a device shipped with a patched version.
Two things routinely defeat identification:
- Remarked or blank parts. Common on cost-reduced consumer gear. Fall back to behavioral identification: count the pins, find the power and ground pins with a multimeter in continuity mode, and compare the surviving pinout against candidate datasheets.
- Package-on-package and BGA. If the markings are on top of a stack, or the pins are underneath, visual identification stops. This is where X-ray, or simply reading the FCC filing for the device, becomes the cheaper path. FCC filings for any radio-bearing device sold in the US are public and frequently include internal photographs.
Original material: the lab notebook
The tools page lists the notebook as the single most important item in the kit, and that is not a joke. The rules — date and sign every page, never remove a page, single-line strikethrough for errors, no white-out — come from patent practice, and they exist so that the notebook is admissible as evidence of what you knew and when you knew it.
In this course the notebook is your submission, kept in Markdown in your GitLab repository. The same principles apply in a slightly different form:
- Commit as you go. Your commit history is the dated, unalterable record. One giant commit at the end of the term destroys that property.
- Record failures. “I tried 115200 baud and got garbage; 9600 worked” is more valuable than the final working command, because it tells the reader what the search space looked like.
- Screenshots include your OdinID. Put a terminal with your username on screen. This is the equivalent of signing the page.
See Technical Writing if Markdown is unfamiliar, and
Using git for the mechanics.
Reading
- Tools of the Trade — the course toolkit, annotated
- Technical Writing
- Using
git - OWASP IoT Security Testing Guide, “IoT Device Model” — the attack-surface decomposition this course follows
Week 2 — UART and instrumentation
Objectives
- Locate a UART on an unknown board.
- Determine baud rate from a captured waveform without guessing.
- Use a logic analyzer to capture and decode an asynchronous serial signal.
- Explain why a 5 V signal into a 3.3 V pin is a problem.
Original material: why UART first
UART is the first interface you look for on any embedded device, and the reason is economic rather than technical. Bringing up a board requires a console. The console is how the firmware engineer found out that the DDR timings were wrong at 3 a.m. six weeks before shipping. Removing that console before production requires someone to decide to remove it, test that nothing depended on it, and absorb the risk of losing field-debug capability. That decision frequently does not get made.
What you find, when it is there, ranges across a spectrum:
| What you get | How common | What it is worth |
|---|---|---|
| Silent pins | Common on newer gear | Nothing directly; try Week 4 |
| Boot log only, no input | Very common | Kernel version, partition layout, hardware inventory |
| Bootloader prompt (U-Boot) | Common | Usually total compromise — see Week 9 |
| Root shell, no password | Still distressingly common | Total compromise |
| Login prompt | Common | A password-guessing target, and a version banner |
Even the “worthless” boot-log-only case hands you the kernel version, the partition table, and often the exact firmware build string — everything you need to go find the vendor’s GPL source drop and skip the reverse engineering entirely.
Original material: finding the pins
A UART is three or four pads: TX, RX, GND, and often VCC. To find them:
- Find ground first, with a multimeter in continuity mode, referenced against a known ground such as a shield can or the negative terminal of a large electrolytic capacitor. Getting ground wrong is how probes die.
- Find VCC — it sits at a steady 3.3 V (or 1.8 V, increasingly) relative to ground and does not move.
- TX is the one that talks at boot. Power-cycle the board with a scope or logic analyzer on the candidate pins. TX shows a burst of activity in the first second or two as the bootloader prints. RX generally idles high and does nothing.
⚠️ Check your voltage domain before connecting anything. Modern SoCs run 3.3 V or 1.8 V I/O. Connecting a 5 V FTDI cable to a 1.8 V pin will, at best, not work, and at worst destroy the pin, the pad, or the part. Measure the idle voltage on TX before you connect. The Tigard has a voltage-select jumper precisely because this mistake is so common.
Worked example: recovering baud rate from a capture
You have a capture of a signal you believe is UART, but the decoder produces garbage at every standard rate you try. Rather than guessing, measure.
UART has no clock line. The receiver recovers timing by assuming a fixed bit
period, so the bit period is the only thing you need. In your logic analyzer,
zoom in and measure the narrowest pulse in the capture — that is one bit
time, because somewhere in the data there is an isolated 1 between two 0s
or vice versa.
narrowest pulse measured: 8.68 µs
baud = 1 / 8.68e-6 = 115,207
nearest standard rate = 115200
Now re-run the decoder at 115200. If it still produces garbage but the frame boundaries look right, the problem is framing rather than rate: try 8N1 first, then 8E1 and 7E1, which still show up on industrial gear.
If the narrowest pulse is not a clean multiple of anything standard, you may be looking at a non-standard rate — U-Boot on some SoCs uses 115200 derived from an odd crystal and lands a percent or two off. Decoders that let you type an arbitrary rate will still lock on.
Reading
- UART — framing, finding the pins, recovering the baud rate, and what a boot log is worth
- Hardware Communications Protocols — where UART sits among the four buses
- Tools of the Trade
- Sigrok/PulseView documentation — the open-source logic-analyzer stack
- Saleae Logic documentation — protocol decoders
Week 3 — I2C and SPI
Objectives
- Decode an I2C transaction, including address, R/W bit, and ACK/NAK.
- Explain why SPI needs four wires and I2C needs two.
- Passively tap a live bus without disturbing it.
- Recognize a SPI flash part and its command set.
Original material: two buses, two philosophies
I2C and SPI solve the same problem — get a microcontroller talking to a peripheral — and make opposite trade-offs. Understanding the trade-off tells you what to expect when you probe.
| I2C | SPI | |
|---|---|---|
| Wires | 2 (SDA, SCL) | 4 (MOSI, MISO, SCK, CS) + 1 CS per device |
| Addressing | 7-bit address in-band | Out-of-band, via chip select |
| Speed | 100 kHz / 400 kHz / 1 MHz typical | 1–100 MHz+ |
| Ack | Receiver pulls SDA low for ACK | None |
| Typical use | Sensors, EEPROMs, RTCs | Flash, displays, radios |
For an attacker the practical consequences are:
- I2C is self-describing. Every transaction carries the target address, so a capture tells you what devices exist without you having to trace the board. An address scan is a legitimate enumeration technique.
- SPI is not. Chip select determines the target, so a capture on MOSI/MISO alone cannot tell you which of three devices was being addressed. You must capture the CS lines too. This trips up nearly everyone the first time.
- SPI is where the firmware lives. The speed advantage means bulk storage
goes on SPI. That 8-pin part beside the SoC is running a near-universal
command set —
0x03read,0x9Fread JEDEC ID,0x02page program,0x20/0xD8erase — which is what makes Week 5 possible.
Worked example: reading a JEDEC ID off a live bus
You have a logic analyzer on a suspected SPI flash. Power-cycle the board and capture the first few milliseconds. Nearly every bootloader begins by identifying the flash part:
CS ‾‾‾\_______________________________/‾‾‾
MOSI 9F 00 00 00
MISO -- EF 40 18
The host sends 0x9F (Read JEDEC ID) followed by three dummy bytes; the flash
replies during those bytes. Decode the reply:
EF = manufacturer -> Winbond
40 = memory type -> W25Q series, SPI
18 = capacity code -> 2^0x18 bytes = 16 MiB = 128 Mbit
So: a Winbond W25Q128, 16 MiB. You now know the exact part, its datasheet, its command set, and the size of the dump you are about to take in Week 5 — all without desoldering anything.
⚠️ Passive means passive. When tapping a live bus, your probes must not drive it. Do not connect a Bus Pirate in active master mode to a bus that already has a master; you will get bus contention, corrupt the transaction, and possibly damage a driver. Sniff first, interact later, and only when the host is held in reset.
Reading
- Hardware Communications Protocols — telling the four buses apart, and choosing which to attack
- I2C and SPI — the bus pages in detail
- NXP UM10204, I2C-bus specification and user manual — the authoritative I2C reference
- JEDEC JESD216 (SFDP) — the standard that lets you query a flash part for its own parameters
Week 4 — JTAG and SWD
Objectives
- Explain the JTAG TAP state machine at a level sufficient to use it.
- Discover JTAG or SWD pins on an unlabeled header.
- Connect a Black Magic Probe and halt a running core.
- Describe how debug lockout is implemented and how it is defeated.
Original material: what JTAG actually gives you
UART gives you whatever the firmware chooses to print. JTAG gives you the silicon. That is the entire difference, and it is enormous: with working JTAG or SWD you can halt the CPU mid-instruction, read and write arbitrary memory including the internal flash, single-step, set hardware breakpoints, and in many cases read out the firmware directly regardless of what the running code wants.
JTAG (IEEE 1149.1) was designed for boundary scan — testing solder joints on assembled boards by shifting bits into a chain of cells around each pin. Debug access was bolted on afterward, which is why the interface feels architecturally strange. SWD is ARM’s two-pin replacement that drops boundary scan and keeps the debug half.
The signals:
| Signal | JTAG | SWD | Role |
|---|---|---|---|
| TCK / SWCLK | ✔ | ✔ | Clock |
| TMS / SWDIO | ✔ | ✔ | Mode select / bidirectional data |
| TDI | ✔ | Data in | |
| TDO | ✔ | Data out | |
| TRST | optional | Reset (often absent) |
Original material: finding the pins when nothing is labeled
Unpopulated headers are the giveaway, but the pin order is arbitrary. Three approaches, in increasing order of effort:
- Guess from the connector. A 10-pin 0.05” header on an ARM board is almost certainly the Cortex Debug connector, which has a standard pinout. A 20-pin 0.1” header is likely classic ARM JTAG. Try the standard pinout first; it costs nothing.
- Brute force with a JTAGulator. The JTAGulator drives every permutation of candidate pins and watches for a valid IDCODE response. This is what it exists for and it is very good at it.
- Boundary-scan the traces. If you have identified the SoC, its datasheet gives the JTAG pin numbers; then it is a continuity-tracing exercise from the package to the header.
The tell for success is an IDCODE: a 32-bit value that every compliant TAP
returns after reset, encoding manufacturer, part number, and revision. A valid
IDCODE with a 1 in the least-significant bit means you have found a working
TAP. All-zeros or all-ones means you have not.
Original material: why lockout so often fails
Vendors can disable debug access. On most microcontroller families this is a fuse, a lock bit, or a protection level stored in one-time-programmable memory. It fails in recognizable patterns:
- Never set. The single most common failure. Setting it is a manufacturing step that costs time and can brick units, so it gets skipped or gets skipped on a production lot.
- Set, but reversible with mass erase. Many parts allow re-enabling debug if you erase the flash. Useless if you want the firmware — but if you want to run your own code on the device, it is exactly what you need.
- Set, but bypassable by fault injection. The lock check is code or logic that runs at boot. Glitch the supply rail or the clock at the right microsecond and the check can be skipped. The ChipWhisperer and Faultier in the course kit exist to demonstrate this. We cover the concept in Week 4 and the practice as a final project option.
- Set on the main core, absent on a second core. Multi-core SoCs and radio co-processors frequently have a debug port nobody remembered to lock.
Reading
- JTAG and SWD — the TAP, IDCODE, pin discovery, and how lockout fails
- IEEE 1149.1 overview material
- ARM Debug Interface Architecture Specification (ADIv5/ADIv6) — SWD protocol
- Black Magic Probe documentation
Week 5 — Firmware extraction from memory
Objectives
- Dump a SPI-NOR flash in-circuit.
- Decide when in-circuit dumping will not work and chip-off is required.
- Use
binwalkto identify and extract embedded filesystems. - Use entropy analysis to distinguish compression from encryption.
Original material: three ways to get firmware, in order of preference
- Download it. Vendor support sites, OTA update endpoints, and GPL source releases. Free, non-destructive, and legal for anything publicly posted. Always try this first; a surprising fraction of assessments end here.
- Read it off the running system. If Week 2 or Week 4 gave you a shell or
debug access,
ddthe MTD partitions out over the console. Non-destructive and gives you the deployed image including device-specific configuration. - Read the flash chip directly. In-circuit with a SOIC-8 clip if the board permits it; chip-off with hot air if it does not. This is where the course spends its bench time because it is the technique that always works.
In-circuit reading fails when the SoC contends for the bus. The standard fixes, in order: hold the SoC in reset; pull its power while keeping the flash powered (risky, and back-powering through protection diodes is a real hazard); desolder the flash. If you find yourself fighting contention for more than an hour, desolder — it is faster than being clever.
Worked example: from raw dump to filesystem
You have dump.bin, 16 MiB, off a W25Q128. First, confirm you got a real dump
rather than 16 MiB of 0xFF:
$ ls -l dump.bin
-rw-r--r-- 1 student student 16777216 Sep 3 14:02 dump.bin
$ xxd dump.bin | head -3
00000000: 27051956 5b91d1d3 66d2a4c1 00600000 '..V[...f....`..
00000010: 001d6f42 00600000 8b5a1c74 05050200 ..oB.`...Z.t....
00000020: 4c696e75 78204b65 726e656c 20496d61 Linux Kernel Ima
0x27051956 is the U-Boot legacy image magic, and the ASCII confirms it. Now
let binwalk map the whole thing:
$ binwalk dump.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 uImage firmware image, header size: 64 bytes,
data size: 1929538 bytes, compression: lzma,
CPU: ARM, OS: Linux, image type: OS Kernel Image
64 0x40 LZMA compressed data, properties: 0x5D
1929602 0x1D7A82 SquashFS file system, little endian, version: 4.0,
compression: xz, image size: 8534016 bytes
--------------------------------------------------------------------------------
Analyzed 1 file for 85 file signatures (187 magic patterns) in 22.0 milliseconds
🛠️ binwalk 3 is a rewrite, and its output moved. Version 3 (Rust) replaced the Python version 2 that most tutorials were written against. The columns are wider, field names now carry colons (
version: 4.0, notversion 4.0), and it prints the signature-count footer above. If a guide’s output does not look like yours, checkbinwalk --versionbefore you doubt your dump.
Extract, then look at what you have:
$ binwalk -e dump.bin
$ ls extractions/dump.bin.extracted/1D7A82/squashfs-root/
bin dev etc lib mnt proc sbin sys tmp usr var www
Note the shape of that path. binwalk 3 writes to
extractions/<file>.extracted/<hex offset>/, one subdirectory per signature it
extracted, named for where in the image it was found. binwalk 2 used
_dump.bin.extracted/ with a flat layout, which is what older write-ups show.
At this point the assessment becomes a Linux one: etc/passwd and
etc/shadow, hardcoded keys under etc/, the web root under www/, and the
init scripts that tell you what listens on the network.
⚠️
binwalk -eruns external extractors on untrusted data. That is a code execution surface pointed at a file you got from a hostile device. Extract inside a container or a throwaway VM, not on your daily driver. The same caution applies to any-e-style recursive extraction.
Worked example: entropy tells you whether to keep going
If binwalk finds nothing but one large blob, the question is whether the
image is compressed, encrypted, or just unrecognized. Entropy answers it:
$ binwalk -E dump.bin
Read the resulting curve as follows:
| Entropy | Meaning |
|---|---|
| ~0.0 | Erased flash (0xFF) or padding |
| 0.3–0.7 | Code and data — normal executable regions |
| ~0.8 | Uncompressed filesystem: executables plus strings and metadata |
| 0.99+ | Compressed or encrypted — the number alone cannot tell you which |
That last row is the one that matters, and it is where the common tutorial advice is wrong. A good compressor’s output is statistically indistinguishable from random data — that is close to the definition of “good compressor.” On the course target, the LZMA-compressed kernel measures 0.9998 and the encrypted keystore measures 0.9996. You cannot separate those, and no threshold you pick will.
So entropy tells you where the interesting regions are, and something else tells you what they are. Three discriminators, in the order you should try them:
- Is there a header? Compressed data in firmware is nearly always wrapped in something — a uImage header, a gzip or LZMA magic, a vendor container. Encrypted regions typically start immediately with ciphertext, because a header would leak structure.
- Does a decompressor accept it? The definitive test. If
unlzmaorgunzipconsumes it and produces sensible output, it was compressed. - What do the edges look like on the plot? Both compressed and encrypted regions sit near 1.0, but a compressed region usually shows a small dip at its header and ragged edges where padding begins. An encrypted region tends to start and stop abruptly at block boundaries.
A maximum-entropy region with sharp edges, no recognizable header, and nothing that will decompress it is the signature of an encrypted image. That does not end the assessment — it moves it. The decryption key has to exist somewhere the device can reach it, which means the bootloader, a secure element, or eFuses. That is a Week 9 problem, and one of the final project options.
Reading
- Dumping flash — three routes, verification, and reading the image
binwalkv3 documentation — note that v3 is a Rust rewrite with different flags from the v2.x Python tool most tutorials describe- OWASP Firmware Security Testing Methodology (FSTM) — the nine-stage process
flashromsupported-hardware list
Week 6 — Networks, WiFi, BLE, and firmware from the wire
Objectives
- Place IoT devices in a normal network threat model.
- Capture and analyze BLE advertising and connection traffic.
- Recover a firmware image from an intercepted OTA update.
- Explain why TLS is frequently absent or broken on embedded devices.
Original material: firmware extraction without touching the hardware
Weeks 2–5 assume physical access. This week removes that assumption. If a device downloads its own updates, and you can see that traffic, the device extracts its firmware for you.
The workflow:
- Get the device onto a network you control. A Raspberry Pi as an access point, or your own AP with a span port. This is what the course gateway Pi is for.
- Capture everything. tcpdump on the gateway, analyzed in Wireshark. See Network Traffic Capture for the mechanics.
- Trigger an update. Factory reset frequently does it, since devices check for updates on first boot.
- Find the transfer. Look for a large HTTP response, or a TLS session with
a large data flow.
tshark -z conv,tcpranks conversations by bytes and finds it immediately. - Carve it out. Wireshark’s File → Export Objects → HTTP does this directly for cleartext. See Pcap Analysis and Manipulation Tools.
The reason this works so often is that firmware-over-HTTP remains common. When TLS is present, the failure modes are their own finding: no certificate validation (so a proxy with a self-signed cert works), a pinned certificate that expired, or validation that checks the chain but not the hostname.
Original material: BLE in one page
Bluetooth Low Energy is not Bluetooth Classic and shares almost nothing with it above the radio. What matters for assessment:
- Advertising is public. Devices broadcast on three advertising channels (37, 38, 39) before any connection exists. Advertisements often leak device type, name, manufacturer, and sometimes sensor values with no pairing at all. This is passive, zero-interaction reconnaissance.
- GATT is the attack surface. Once connected, the Generic Attribute Profile
exposes services and characteristics — a key-value store with permissions.
Enumerate it and you have the device’s entire control API.
bluetoothctlandnRF Connectboth do this. - Pairing is where the security is, and it is frequently skipped. “Just Works” pairing provides no MITM protection. Many devices then implement authentication in the application layer above GATT, badly.
- Sniffing a connection is harder than sniffing advertisements, because BLE channel-hops. Dedicated hardware (Ubertooth One, nRF52 with sniffer firmware) follows the hop sequence; a general SDR does not, without work.
Reading
- Networking Fundamentals
- tcpdump, Wireshark, tshark
- Network Traffic Capture, Pcap Analysis and Manipulation Tools
- WiFi — the four-way handshake, PMKID, WPA3 transition mode, provisioning
- Bluetooth — Classic and LE, GATT enumeration, pairing models
- Bluetooth Core Specification — GAP and GATT sections
- The Art of Packet Crafting with Scapy, and the course Scapy page
Week 7 — RF capture and spectrum analysis
Objectives
- Explain sampling, IQ data, and why bandwidth costs you sample rate.
- Find an unknown transmitter in the spectrum.
- Identify a modulation scheme from its spectral and time-domain signature.
- Record IQ to a file and analyze it offline.
Original material: the three numbers that define an SDR capture
Every capture is characterized by three numbers, and getting them wrong wastes the capture:
- Center frequency. Where you are looking. If the signal is not within your bandwidth of this, you will not see it.
- Sample rate, which sets bandwidth. Nyquist says you can observe a bandwidth equal to your complex sample rate. A 2 MS/s capture sees 2 MHz of spectrum. To see a 125 kHz LoRa channel you need at least 125 kS/s, and realistically 250 kS/s to have margin.
- Gain. Too little and the signal is in the noise; too much and the front-end clips, producing spurious signals across the whole band that look like real transmitters and are not.
The characteristic beginner failure is turning gain to maximum, seeing signals everywhere, and chasing artifacts. Set gain so the noise floor is visible and the strongest signal does not saturate.
Original material: identifying modulation by eye
Before demodulating anything, classify it. A waterfall and a time-domain view get you most of the way:
| Appearance | Likely modulation | Common in |
|---|---|---|
| Single line, blinks on/off | OOK / ASK | Cheap remotes, doorbells, TPMS |
| Two parallel lines alternating | 2-FSK | Sub-GHz sensors, garage doors |
| A band that fills solidly | Wideband FSK/GFSK | BLE, proprietary |
| Diagonal sweeps, sawtooth | Chirp spread spectrum | LoRa |
| Hopping across a band | FHSS | Bluetooth Classic, some industrial |
The diagonal-sweep signature is diagnostic and is why LoRa is a good teaching target: you can see the chirps in a waterfall, which makes the modulation concrete in a way that a constellation diagram does not.
⚠️ Receiving is not transmitting. Passive reception across most of the spectrum is legal in the US; transmitting is licensed, and transmitting on bands you are not licensed for is a federal offence. The 915 MHz ISM band our hardware uses permits unlicensed transmission under Part 15 rules with power and duty-cycle limits. Stay inside them, and do not transmit on anything else.
Reading
- Radio Protocols — SDR fundamentals and modulation identification
- GNU Radio tutorials — the signal-processing foundation
- Universal Radio Hacker (URH) documentation and the accompanying USENIX WOOT ‘18 paper
rtl-sdrproject documentation
Week 8 — Radio hacking: LoRa, Zigbee, Thread, and Matter
Objectives
- Distinguish LoRa from LoRaWAN and explain why the difference is a security boundary.
- Capture and replay a LoRa transmission.
- Describe Zigbee’s key management and its historical failure modes.
- Explain what Matter changed and what it did not.
Original material: LoRa is not LoRaWAN, and it matters
This is the single most common confusion in the field, and it is a security distinction rather than a pedantic one.
LoRa is a physical layer: chirp spread spectrum modulation, patented by Semtech, implemented in the RFM95 on your Feather. It provides no security whatsoever. Bytes in, RF out. Anything you transmit is readable by anyone with a matching radio, and anything you receive can be forged by anyone with a matching radio.
LoRaWAN is a MAC layer and network architecture built on top of LoRa. It specifies device join procedures, two layers of AES-128 keys (a network session key for integrity, an application session key for confidentiality), and — the part that matters for replay — frame counters.
The consequence for Labs 2 and 3, and for HW #4:
- Raw LoRa traffic between your boards is trivially replayable. Capture the bytes, retransmit them, and the receiver cannot tell the difference. This is not a flaw in your code; it is the absence of a MAC layer.
- Properly implemented LoRaWAN rejects the replay because the frame counter has already been used. Improperly implemented LoRaWAN — counters reset on reboot, counters not checked, ABP activation with static keys — does not, and this has been a recurring real-world finding.
The teaching point generalizes: replay protection is state, and embedded devices are bad at state. A counter that must survive power loss has to live in flash, flash has limited write endurance, so implementers cut corners, and the corner they cut is usually the one that protects you.
Original material: Zigbee, Thread, and Matter in context
All three sit on IEEE 802.15.4 as a physical and MAC layer. What differs is everything above it.
- Zigbee adds its own network and application layers. Its historical weakness was key transport: joining devices could receive the network key encrypted with a well-known default link key, so anyone sniffing the join could recover the network key. Later versions and install codes address this, but deployed devices are long-lived.
- Thread replaces Zigbee’s networking with IPv6 (6LoWPAN), giving mesh routing with real IP semantics. Security is DTLS-based commissioning and network-wide keys.
- Matter is an application layer that runs over Thread, WiFi, or Ethernet, standardizing device types and commissioning. It mandates per-device attestation certificates — every certified device ships with a factory certificate proving it is a genuine device of its claimed type. That is a genuine improvement over everything preceding it.
What Matter did not change: the device still runs firmware, that firmware still lives in a flash chip you can read, and attestation says nothing about whether the application logic above it is correct. The hardware techniques from Weeks 1–5 are entirely unaffected by Matter certification.
🛠️ Matter versions move quickly — 1.4 (Nov 2024), 1.4.1, 1.4.2, 1.5 (Nov 2025), 1.6 (2026). Check the current specification version before citing features.
Reading
- Radio Protocols — SDR fundamentals and the recurring failure patterns
- LoRa and LoRaWAN — chirp spread spectrum, OTAA vs ABP, frame counters
- Zigbee — the shared network key, default link keys, Touchlink
- Z-Wave — S0’s all-zeros bootstrap, S2 and DSK, the Z-Shave downgrade
- Thread and Matter — J-PAKE commissioning, attestation, fabrics
- Other IoT radios — rolling codes, TPMS, nRF24, and undocumented protocols
- LoRa Alliance, LoRaWAN Specification and the LoRaWAN security whitepaper
- Connectivity Standards Alliance — Matter specification and Zigbee documents
- Thread Group specification overview
Week 9 — Software reverse engineering and bootloaders
Objectives
- Load a raw firmware blob into Ghidra with correct architecture and base address.
- Locate interesting functionality without symbols.
- Explain the secure boot chain of trust and where it breaks.
- Describe U-Boot’s role as an attack surface.
Original material: the base address problem
Reverse engineering a Linux binary is comparatively easy: ELF headers declare the architecture, the entry point, and the section layout. A raw firmware dump declares nothing. You have a flat array of bytes and three unknowns:
- Architecture and endianness. From Week 1’s chip identification, or by letting a tool guess from instruction-frequency statistics.
- Load address. Where the code believes it lives. Get this wrong and every absolute pointer in the binary points at nothing, so no cross-references resolve and the listing is unreadable.
- Entry point. Where execution begins.
The load address is the one that blocks progress, and the standard trick is to recover it from the data rather than guess. Scan the image for 32-bit values that look like pointers — they cluster tightly around the true load base, because pointer tables, string references, and vector tables all use absolute addresses. Histogram the high bytes of every aligned 32-bit word; the spike is your base address.
On ARM Cortex-M there is a shortcut: the image begins with a vector table whose first word is the initial stack pointer and whose second word is the reset handler address. Read the second word, mask off the Thumb bit, and you have both the entry point and a strong hint at the load base.
Worked example: orienting in an unfamiliar image
Once loaded, you have no symbols. Find your footing through strings:
$ strings -n 8 -t x firmware.bin | grep -iE 'passw|key|http|/dev/|admin'
1a4f0 /dev/mtdblock3
1a53c Login incorrect
1a558 http://update.example.net/fw/check
1b2c4 %s: bad password for %s
⚠️
-n 8hides exactly the tokens you care most about. The grep above asks foradmin, butadminis five characters and never reaches the grep —stringsdropped it. Short, high-value words (admin,root,key,pw) all sit under the default threshold of 4 as well as under 8. Run the sweep twice: once at-n 8for readable sentences and URLs, once at-n 4when you are hunting a specific short token, and accept the noise on the second pass.
Each hit is an anchor. In Ghidra, jump to the address, find the cross-reference, and you are inside the function that uses it — the login handler, the update checker, the partition mount. Working outward from string references is far faster than reading from the entry point down, and it goes straight to the code that matters.
Cross-reference this with the filesystem you extracted in Week 5: if
/dev/mtdblock3 appears in both the binary and the init scripts, you have
connected the running configuration to the code that consumes it.
Original material: secure boot and where the chain breaks
Secure boot is a chain: an immutable root of trust in ROM verifies the bootloader, which verifies the kernel, which verifies the root filesystem. Each link checks a signature before transferring control.
Real-world break points, roughly in order of frequency:
- The chain is not enabled. The silicon supports it; the fuse was never blown.
- The chain starts too late. ROM verifies the first-stage bootloader, but the first-stage does not verify the second, or U-Boot does not verify the kernel. One unverified link breaks everything downstream.
- Verification happens but the result is ignored. Signature is checked, a failure is logged, boot continues.
- The bootloader is interactive. U-Boot with an unrestricted console lets
you change
bootargsto addinit=/bin/sh, ortftpbootan unsigned kernel. A signed kernel does not help if you can tell the bootloader to load a different one — this is why the UART console from Week 2 is so valuable. - Time-of-check to time-of-use. The image is verified in flash, then copied to RAM, and the copy is what executes. A glitch or a DMA write between the two is a live attack.
Reading
- Software Reverse Engineering — the base-address problem, orienting without symbols, bootloaders
- Ghidra documentation and the NSA course materials
- U-Boot documentation — environment,
bootargs, and verified boot (FIT images) - Memory Corruption and Exploit Mitigations
- Secure C
Week 10 — IoT operating systems and secure architecture
Objectives
- Compare the major embedded OS families by security model.
- Explain what an MMU buys you and what its absence costs.
- Evaluate a device against a published baseline.
- Present findings the way a real assessment would.
Original material: the OS landscape, by threat model
The question that separates these systems is not features but isolation.
| System | Isolation | Update story | Notes for assessment |
|---|---|---|---|
| OpenWRT / embedded Linux | Full MMU, processes, users | Package manager, vendor images | Everything you know about Linux applies. Usually the richest target. |
| FreeRTOS | None by default (MPU optional) | Whatever the vendor built | A scheduler, not an OS. One task can corrupt another’s memory. No users, no permissions. |
| Zephyr | MPU-based, userspace option | MCUboot, well-specified | Modern, security-conscious, increasingly the FreeRTOS alternative. |
| VxWorks | MMU on supported targets | Vendor-controlled | Real-time, safety-critical, long-lived. The URGENT/11 TCP/IP stack vulnerabilities showed how far a bug propagates in this ecosystem. |
| Arm Mbed OS | MPU (uVisor discontinued) | Mbed tooling | End of life July 2026. Present in deployed devices for years to come; not a choice for new work. |
| Windows IoT | Full NT security model | Windows Update | Windows 10 IoT Enterprise LTSC 2021 mainstream support ends January 2027. Heavy, but genuinely hardened. |
The practical consequence for an assessment: on a no-MMU system, any memory corruption bug is total compromise. There is no privilege boundary to cross, no ASLR, frequently no stack canaries, and the exploit mitigations covered in Memory Corruption largely do not exist. A stack overflow in a FreeRTOS network task is game over in a way it has not been on desktop Linux since roughly 2004.
Original material: baselines, and using them as a report structure
Three documents define what “reasonably secure” means for consumer IoT, and any of them makes a defensible structure for a findings report:
- ETSI EN 303 645 — the European consumer IoT baseline. Thirteen outcome-focused provisions: no universal default passwords, a vulnerability disclosure policy, keep software updated, securely store sensitive security parameters, and so on. Outcome-focused means it is testable.
- NIST IR 8259A — the US device-capability core baseline, aimed at manufacturers. Six capability areas including device identification, configuration, data protection, and software update.
- OWASP IoT Security Testing Guide (ISTG) — a testing methodology rather than a requirements list, decomposing a device into attack surfaces (processing unit, memory, firmware, data exchange services, internal/external interfaces, wireless) and specifying test cases per surface.
The ISTG device model maps almost one-to-one onto this course: its memory and firmware surfaces are Weeks 4–5, its wireless interfaces are Weeks 7–8, its internal interfaces are Weeks 2–3. Structuring your final report around it demonstrates coverage rather than asserting it.
The older OWASP IoT Top 10 (2018) remains widely cited and is still a reasonable vocabulary for describing findings, but it is a risk list rather than a methodology and has not been revised in some years — prefer the ISTG for structuring actual testing.
Reading
- Attacks — vulnerability classes, fault injection, baselines, and writing findings up
- ETSI EN 303 645 — Cyber Security for Consumer Internet of Things: Baseline Requirements
- NIST IR 8259A — IoT Device Cybersecurity Capability Core Baseline
- OWASP IoT Security Testing Guide
- Threat Modeling
- Software Supply-Chain Security
Assignments at a glance
Five assignments, each covering two weeks of material and each building on the last. Details and point breakdowns are on the individual pages.
| # | Covers | Weeks | Deliverable |
|---|---|---|---|
| HW #1 | Bench bring-up, chip and pin identification, UART discovery | 1–2 | hw1/hw1.md |
| HW #2 | I2C and SPI bus analysis, JTAG/SWD discovery | 3–4 | hw2/hw2.md |
| HW #3 | Firmware extraction from flash and from the wire | 5–6 | hw3/hw3.md |
| HW #4 | RF capture, LoRa analysis, replay | 7–8 | hw4/hw4.md |
| HW #5 | Firmware reverse engineering and bootloader analysis | 9–10 | hw5/hw5.md |
The final project offers three options with different audiences; read that page early, because the graduate-track option benefits from being chosen by Week 4.
Key takeaways
- The assessment order — identify, probe, capture, extract, analyze — is not arbitrary; each stage supplies a prerequisite for the next.
- UART and unpopulated debug headers persist because removing them costs money and provides no customer-visible benefit.
- SPI is where firmware lives, which makes the JEDEC ID query the highest-value four bytes you will send all term.
- Entropy distinguishes compression from encryption, and that distinction decides whether the next step is extraction or key recovery.
- LoRa provides no security; LoRaWAN provides replay protection through frame counters, and embedded devices are historically bad at maintaining counters.
- On a no-MMU RTOS, memory corruption is immediate total compromise — the mitigations you rely on elsewhere are simply absent.
References
- OWASP IoT Security Testing Guide — https://owasp.org/www-project-iot-security-testing-guide/
- OWASP Internet of Things Project — https://owasp.org/www-project-internet-of-things/
- OWASP Firmware Security Testing Methodology — https://github.com/scriptingxss/owasp-fstm
- OWASP IoTGoat (deliberately vulnerable firmware) — https://github.com/OWASP/IoTGoat
- ETSI EN 303 645, Cyber Security for Consumer IoT — https://www.etsi.org/deliver/etsi_en/303600_303699/303645/
- NIST IR 8259A, IoT Device Cybersecurity Capability Core Baseline — https://nvlpubs.nist.gov/nistpubs/ir/2020/NIST.IR.8259A.pdf
- NIST Cybersecurity for IoT Program — https://www.nist.gov/itl/applied-cybersecurity/nist-cybersecurity-iot-program/resources
- binwalk (v3, Rust) — https://github.com/ReFirmLabs/binwalk
- flashrom — https://www.flashrom.org/
- sigrok / PulseView — https://sigrok.org/
- Ghidra — https://ghidra-sre.org/
- Universal Radio Hacker — https://github.com/jopohl/urh
- Universal Radio Hacker (USENIX WOOT ‘18 paper) — https://www.usenix.org/system/files/conference/woot18/woot18-paper-pohl.pdf
- GNU Radio — https://www.gnuradio.org/
- rtl-sdr — https://www.rtl-sdr.com/
- NXP UM10204, I2C-bus specification — https://www.pololu.com/file/0J435/UM10204.pdf
- LoRa Alliance — https://lora-alliance.org/resource-hub/
- Connectivity Standards Alliance (Matter, Zigbee) — https://csa-iot.org/
- Thread Group — https://www.threadgroup.org/
- U-Boot documentation — https://docs.u-boot.org/
- Zephyr Project — https://www.zephyrproject.org/
- FreeRTOS — https://www.freertos.org/
- Arm Mbed end-of-life notice — https://os.mbed.com/blog/entry/Important-Update-on-Mbed/
- Windows 10 IoT Enterprise lifecycle — https://learn.microsoft.com/en-us/lifecycle/products/windows-10-iot-enterprise
- JTAGulator — https://grandideastudio.com/portfolio/security/jtagulator/
- Black Magic Probe — https://black-magic.org/
- ChipWhisperer — https://www.newae.com/chipwhisperer
- Adafruit Feather RP2040 RFM95 documentation — https://learn.adafruit.com/feather-rp2040-rfm95
Related course pages: Course home · Tools of the Trade · Final Project · Networking Fundamentals · Wireshark · Threat Modeling
🛠️ Maintenance note: the fastest-moving items on this page are (a) the Matter specification version, which has shipped six releases since late 2024; (b)
binwalk, whose v3 Rust rewrite changed the CLI relative to nearly every tutorial online; (c) OS lifecycle dates — Mbed OS reached end of life in July 2026 and Windows 10 IoT Enterprise LTSC 2021 mainstream support ends January 2027; and (d) hardware availability, since several tools in the course kit are small-batch products. Re-check these before each offering. Assignment due dates are deliberately expressed as week numbers so only the term calendar needs updating.