Maker.io main logo

How Hardware Gets Hacked (Part 4): Memory Protections

2026-05-20 | By Nathan Jones

Where did we leave off?

In the previous article, I discussed how to take the perspective of an attacker: identifying the attacker-controlled inputs that could be manipulated to exploit a vulnerability and achieve a payoff.

Image of How Hardware Gets Hacked (Part 4): Memory Protections

πŸ’‘ Let's now apply that mindset to our own device.

Reflecting on the code we have now for our car and key fob device, what are some possible attacks that opposing teams could conduct in order to extract our flags? Record at least three ideas on a piece of paper or in a text document before moving on. A simple way to evaluate which attack to attempt first is to plot our ideas along two criteria/axes, either as a two-dimensional graph or a two-column table.

Image of How Hardware Gets Hacked (Part 4): Memory Protections

Image of How Hardware Gets Hacked (Part 4): Memory Protections

We'd be smart to start with attacks in the upper right-hand corner of our graph, as they have the highest chance of success and are the easiest to conduct.

πŸ€” Rank your ideas now.

Rank the ideas you came up with in the first question above, either using a graph or a table.

If you skipped over the first question in this article (what?!), here are some possible ideas that you can evaluate for their difficulty and likelihood of success.

  • Send an "unlock" command to a car from a serial adapter connected to our computer
  • Unlock a car using the fob for Car #0
  • Bombard the car's serial port with serial data to overwhelm the code, causing a fault that dumps the flags
  • Connect to the device's debug port and read the flags out of memory
  • Send serial data to the car that overwrites the receive buffer, causing the car to execute a call to uart_write with the memory address of the flags

Which attack would you start with?

With the current system design, it's my conclusion that the easiest way to "unlock" a car or "enable" a feature is to read out those flags over an unlocked debugging port!

❗️ Don't make this mistake!

Reading memory off the device is only actually the easiest attack because the competition binaries were encrypted, and they were only decrypted by the MITRE bootloader in the act of flashing it to a competition board. If the binaries were instead given out unencrypted, then the easiest attack would actually just be to read the data straight out of the binary! The first defensive lesson is actually this:

Never give your attackers access to unencrypted data that contains sensitive information (such as flags or secret keys). If you need to distribute programs to your customers but don't plan on implementing a bootloader to decrypt your encrypted program (as in the competition), then you must find other means of obfuscating your sensitive information, such as encrypting just your secrets in flash and only decrypting them at run-time.

Attack #1: Read out memory over JTAG/SWD

In this attack, we’re simply going to start a debug session with our target board. The flags are stored at known locations in memory, so all we need to do is read out that memory to get the flags.

Reading memory like this gives us access to every flag for every attack scenario: the unlock flags for Cars #1-5 and the feature 2 flag for Car #5.

STM32

πŸ“ NOTE: In my first implementations, the flags for the STM32 were not previously at a defined location; they were merely local to the loadFlag function. To mimic the competition setup, this was changed so that the flags were stored at a defined place in flash memory. A section of flash (sector 6; starting address 0x08060000) was set aside in the linker script, and a struct containing all of the flags placed there. This way, all flags on the STM32 are now located in the 256 bytes starting at the beginning of sector 6, 0x08060000.

Since the flags are stored in flash on the STM32, the steps for this attack are the same as if you were just debugging the device yourself:

  1. Start a GDB server connected to the device.
  2. Start GDB (gdb-multiarch) or gdbgui.
  3. Print all flags (The x/256cb demonstrated in the example code tells GDB to print 256 character bytes [cb] beginning at the memory address that follows the e[x]amine command.)

Ex:

Copy Code
$ ./tools/openocd.py debug stm32 {serial_num}
# In a separate window:
$ gdbgui -g "gdb-multiarch -ex 'target remote localhost:3333'"
(gdb) x/256cb 0x08060000
0x8060000 <flags_data>:         100 'd' 101 'e' 102 'f'97 'a'   117 'u' 108 'l' 116 't' 95 '_'
0x8060008 <flags_data+8>:       117 'u' 110 'n'108 'l'  111 'o' 99 'c'  107 'k' 0 '\000'       0 '\000'
0x8060010 <flags_data+16>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
0x8060018 <flags_data+24>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
0x8060020 <flags_data+32>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
0x8060028 <flags_data+40>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
0x8060030 <flags_data+48>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
0x8060038 <flags_data+56>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
0x8060040 <flags_data+64>:      100 'd' 101 'e'102 'f'  97 'a'  117 'u' 108 'l' 116 't' 95 '_'
0x8060048 <flags_data+72>:      102 'f' 101 'e'97 'a'   116 't' 117 'u' 114 'r' 101 'e' 49 '1'
0x8060050 <flags_data+80>:      0 '\000'       0 '\000' 0 '\000'        0 '\000'        0 '\000'0 '\000'        0 '\000'        0 '\000'
...

TM4C

On this device, the location of the flags was set by the competition’s Technical Specifications (section 1.4.3).

Image of How Hardware Gets Hacked (Part 4): Memory Protections

However, the EEPROM can't be read directly from GDB (since it's not part of the memory space), but it can be read using the EEPROM controller peripheral on the TM4C. This is done by setting the EEBLOCK and EEOFFSET registers appropriately and then reading data (32 bits / 1 word at a time) out of EERDWR (or EERDWRINC).

Image of How Hardware Gets Hacked (Part 4): Memory Protections

Following the code in EEPROMRead() from the Tivaware library (in eeprom.c), I had Claude help me write a TCL script that OpenOCD can easily invoke to read an entire section of EEPROM in one go. You can run it like this.

  1. Start a GDB server connected to the device.
  2. Connect via telnet in a separate window.
  3. Load eeprom.tcl and run eeprom_read, giving it a file name (to put the data), the starting address in EEPROM, and the number of bytes to read.

Ex:

Copy Code
$ ./tools/openocd.py debug tm4c {serial_num}
# In a separate window
$ telnet localhost 4444
> halt
> script tools/eeprom.tcl
> eeprom_read flags_dump.bin 0x700 256 

We can then inspect the contents of our file using hexdump.

Copy Code
$ hexdump -C flags_dump.bin
00000000  64 65 66 61 75 6c 74 5f  66 65 61 74 75 72 65 33  |default_feature3|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000040  64 65 66 61 75 6c 74 5f  66 65 61 74 75 72 65 32  |default_feature2|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000080  64 65 66 61 75 6c 74 5f  66 65 61 74 75 72 65 31  |default_feature1|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000c0  64 65 66 61 75 6c 74 5f  75 6e 6c 6f 63 6b 00 00  |default_unlock..|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100 

πŸ”¨ Your turn!

If you have one of the STM32 or TM4C development boards, pull it out now and execute this attack! Don't worry, I'll wait. πŸ˜‰

So, how do we stop it?

Let's brainstorm how we might defend against this attack. Keep in mind that the exact steps for the attack are as follows.

Image of How Hardware Gets Hacked (Part 4): Memory Protections https://www.playembedded.org/blog/debugging-stm32-chibistudio/

  1. Connect a debug adapter to the programming pins on the device.
  2. Start a GDB server.
  3. Send commands (via GDB) to the debug controller on the device to read a section of flash or EEPROM memory.
  4. The debug controller reads the indicated memory locations and returns the data to the debug adapter, which delivers it to GDB.

Preventing any of those steps halts the attack.

πŸ€” Your turn!

Brainstorm a few ways you could prevent that attack. Don't stop until you have at least five (no matter how crazy they are!). Write them down in your notes.

What did you come up with? You may have come up with a number of attacks that are completely viable for real devices but not exactly suitable for the eCTF competition, such as the following.

  • Add tamper-protection to the device, disabling the device entirely if it detects someone attempting to open the enclosure or attaching to the debug port.
  • Use a microcontroller in production that has no debug port; the program is baked into the silicon at manufacturing.
  • Use a proprietary debug adapter or debug protocol.

If so, I applaud you! These are all great ideas, and though we can't exactly implement them for the eCTF competition, they are excellent to keep in mind for production devices.

Did you think at any point, "Surely there must be a way to disable the debug port in production!?"

Image of How Hardware Gets Hacked (Part 4): Memory Protections

There are many security mechanisms that modern microcontrollers provide to developers, though they vary widely by manufacturer and device. Here's what's available on our two devices.

STM32

  • Three levels of read protection (RDP) (See section 3.6.3 of the reference manual for more info.)
    • At RDP 0, the device is fully open.
    • At RDP 1, all flash memory accesses (read, write, erase) trigger a BusFault if a debug adapter is attached.
      • Note: A debugger can still be attached, and the debugger can still read/write the core processor registers and RAM.
    • At RDP 2, flash memory access is prohibited when a debug adapter is attached (as at RDP 1), and a few other protections are also enabled (user option bytes cannot be altered, cannot boot from RAM or system memory bootloader, etc.)
      • Importantly, setting RDP to level 2 is permanent and cannot be undone.
  • Proprietary code readout protection (PCROP) (See section 3.6.5 of the reference manual for more info.)
    • Prevents all flash data from being read, including by the CPU. Any debug access to flash or any LD instructions being executed by the program is/are prevented.
    • Can be enabled/disabled for any of the flash sectors on the STM32 (sectors 0-7 for the STM32F411).
    • (Preventing even CPU reads from flash might not seem useful, but, critically, instruction accesses aren't blocked, so any constants that are baked into the instructions themselves can still be used. For example, this wouldn't work.
    Copy Code
    void getUnlockFlag(uint8_t* dest)
    {
      memcpy(dest, flags_data.unlock, UNLOCK_SIZE);
    } 

    but this would

    Copy Code
    void getUnlockFlag(uint8_t* dest)
    {
      dest[0] = 'd';
      dest[1] = 'e';
      dest[2] = 'f';
      dest[3] = 'a';
      dest[4] = 'u';
      dest[5] = 'l';
      dest[6] = 't';
      dest[7] = '_';
      // ...
    }

    This feature could be used to create protected regions of flash, inside which critical cryptographic operations are allowed to run, but no data [like secret keys] ever really leaves.)

    • Memory protection unit (MPU) (See section 4.2 of the programming manual for more info.)
      • Can make certain sections of flash memory read-only, write-only, execute-only, or some combination of those.

    TM4C

    • EEPROM locking and hiding (See section 8.2.4.1 of the datasheet for more info.)
      • Both individual blocks and the entire EEPROM can be locked with a password, preventing either reads or writes or both until the correct password is entered.
      • Every block except block 0 can also be temporarily hidden, meaning code cannot access that block for reading or writing until a reset (or until the block is explicitly unhidden).
      • These protections are most useful for preventing accidental accesses to the EEPROM, but they don't really constitute a good defense against attackers, at least not by themselves. This is because our attackers have access to both our source code and, unless it's been disabled, the debug port on our microcontroller. The EEPROM password can be easily discovered this way, and "hidden" EEPROM blocks can easily be unhidden. Relying on these for protection against an attacker is like locking the front door of your house and then placing the key on your doorstep.
    • DBG bits of BOOTCFG register (See section 8.2.3.5. of the datasheet for more info.)
      • Will disable the debug port if not set to 0b10.
    • Flash memory protection read enable (FMPRE) (See section 8.2.3.2 - 8.2.3.4 of the datasheet for more info.)
      • Acts similarly to the PCROP on the STM32: prevents all flash reads by a debug adapter or the CPU.
      • Can be enabled on 2 kB-long blocks of flash memory (not EEPROM memory, though!)
    • MPU (See section 3.1.4 of the datasheet for more info.)
      • Similar to the MPU on the STM32.

    ⚠️ Potential Pitfall

    Changing the debug pins to GPIO pins in the software is NOT sufficient to disable the debug interface!

    Ex, for TM4C:

    Copy Code
    void change_debug_pins_to_gpio(void)
    {
      // PC0=TCK/SWCLK, PC1=TMS/SWDIO, PC2=TDI, PC3=TDO/SWO on TM4C
      // 1) Enable clock to GPIO peripheral
      //
      SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
      while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOC));
    
      // 2) Unlock Port C
      //
      HWREG(GPIO_PORTC_BASE + GPIO_O_LOCK)  = GPIO_LOCK_KEY;
      HWREG(GPIO_PORTC_BASE + GPIO_O_CR)   |= GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 |   GPIO_PIN_3;
      HWREG(GPIO_PORTC_BASE + GPIO_O_LOCK)  = 0;
    
      // 3) Set pins as input
      //
      GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
      }

    Although this prevents debug access after the pins became GPIOs, any debug adapter out there is capable of resetting the device (at which point the debug pins are reset to being debug pins again!) and then connecting before the pins are remapped in the firmware to something else. Remapping debug pins to GPIO pins gives you more GPIO pins; it doesn't give you any better security.

    We could also simply make things more difficult for our attacker by implementing some or all of the following defenses.

    • Disable debug info for production builds
    • Hide the source code
    • Hide any map files
    • Don't let users build firmware to run on your device
    • Encrypt the flags in flash and only decrypt them inside loadFlags after a successful unlock attempt

    None of these will prevent an attacker from conducting the attack above on their own, but they can make it harder to successfully pull off.

    Ultimately, we want to implement the defense that definitively stops this specific attack (and no others), while being no more complicated than is necessary. Ideally, the solution also doesn't brick our device, either. For me, that means we want to Disable the debug port using the DBG bits of BOOTCFG (TM4C) or by setting RDP to level 1 (STM32).

    ❓ Why don't we just implement all of the defenses??

    Although adding protections from FMPRE/PCROP or the MPU seems like a good idea at this time, I would discourage you from implementing any defense unless you can demonstrate a realistic attack that that defense would help prevent (like we've done so far in this article). Adding additional defenses "because ... security" runs the risk of creating a defensive posture which itself is so complicated that we don't fully understand it, possibly introducing new vulnerabilities where those defenses interact in unanticipated ways. It may also simply eat up valuable development time if the attack you're trying to defend against isn't one you'd realistically even need to protect from.

    You may have heard of TDD (test-driven development). In this article series, we're going to follow ADD: "attack-driven development"!

    Defense #1: Disable the debug port

    Locking

    STM32

    Enabling RDP 1 on the STM32 is very simple on OpenOCD, which exposes a command to do this exact thing: stm32f2x lock 0.

    The STM32 requires a full power cycle (unplug the development board and plug it back in) for the new RDP settings to take effect.

    TM4C

    Disabling the debug port on the TM4C is only a hair more difficult. It essentially only requires clearing those bottom two bits of BOOTCFG (for which the microcontroller makes us write magic numbers to two other registers to prevent any accidental writes to BOOTCFG), but we have to do this manually in OpenOCD using mmw (memory modify word) and mww (memory write word) as there isn't a special command for it. (See section 8.2.3.10 of the datasheet for more info.)

    The TM4C requires a hardware reset (pressing the reset button on the development board is fine) for the new BOOTCFG settings to take effect.

    But did it work?

    I've added both sequences to the openocd.py script, invoked using the lock subcommand.

    Copy Code
    ./tools/openocd.py lock {stm32|tm4c} {SN}

    I.e.

    Copy Code
    ./tools/openocd.py lock stm32 0672FF495157808667174529

    Lo and behold, it works! Once either device is locked, I either can't connect with GDB at all (TM4C; below top) or I can connect with GDB but I can't print any values from flash (STM32; below bottom).

    TM4C

    Image of How Hardware Gets Hacked (Part 4): Memory Protections

    STM32

    Image of How Hardware Gets Hacked (Part 4): Memory Protections

    πŸ€” Are you satisfied?

    Does this constitute proof that I've closed off the attack vector described above? What else, if anything, would you need to be confident that this defense does what it's intended to do? Jot down your thoughts before continuing on.

    Unlocking

    An unlocking procedure is required so that we don't accidentally brick our device. Luckily for us, performing an unlock procedure on both the STM32 and the TM4C concurrently conducts a mass erase of each device's memory, so an attacker can't extract any flags by unlocking a locked device.

    STM32

    Disabling RDP 1 on the STM32 is very simple in OpenOCD, using a variation of the command to enable RDP 1: stm32f2x unlock 0. I've added this sequence to openocd.py as the unlock command.

    Copy Code
    ./tools/openocd.py unlock stm32 0672FF495157808667174529

    Downgrading the RDP level from 1 to 0 will conduct a mass erase of the device's flash. Changes take effect immediately (no reset or power-cycle required).

    TM4C

    Re-instating the DBG bits in the BOOTCFG register of the TM4C is, unfortunately, a bit more complicated. Although OpenOCD does have the ability to do this (using the command stellaris recover), the OpenOCD driver for the debug adapter on the competition board (ti_icdi.c) fails to execute any OpenOCD command if it can't connect to the target device, which it can't if we've disabled the debug port! Luckily for us, all stellaris recover was doing was sending an unlock message to the ICDI debug adapter on the TM4C (which is literally "debug unlock", formatted as a GDB remote serial protocol [RSP] packet). We can send that message directly to the ICDI adapter ourselves with a little bit of work, bypassing OpenOCD entirely and its "buggy" ICDI driver.

    I had Claude write the Python script to do this, called icdi_unlock.py. It only requires the TM4C's serial number to work.

    Copy Code
    ./tools/icdi_unlock.py {SN}

    I.e.

    Copy Code
    ./tools/icdi_unlock.py 0E236CE6

    As for the STM32, reinstating the debug port on the TM4C will conduct a mass erase of the device's flash, which also restores the FMPRE, FMPPE, and USER_REG registers to their default values. The TM4C requires a full power cycle (unplug the development board and plug it back in) for the new settings to take effect.

    What if I'm looking for a variable in the future that isn't at a specific memory location?

    This is an important question, since pinning a device's secrets to a specific memory location (and also publishing that location!) is definitely a "competition-ism". If we have debug access to a device but don't know exactly where on that device we want to inspect memory to find the secrets, we have a few options based on what other information we do have.

    • Do we have or can we make a MAP file? If yes, then we can search the map file to find where our named variable was stored. Ex:
    Copy Code
    $ cat hardware/stm32/build/car_1234/STM32.map | grep flags
    .flags          0x0000000008060000      0x100
    *(.flags)
    .flags         0x0000000008060000      0x100 hardware/stm32/build/car_1234/Core/Src/main.o
    • If we don't have an ELF with debug info or a MAP file, then our last option is to dump the microcontroller's flash memory. We can do this either from inside GDB or directly using OpenOCD:
      • In GDB use dump binary memory {FILE} {START} {END}. E.g., for STM32 (flash starts at 0x08000000 and is 512 kB or 0x00080000 long):

    Copy Code

    • In GDB use dump binary memory {FILE} {START} {END}. E.g., for STM32 (flash starts at 0x08000000 and is 512 kB or 0x00080000 long):
    Copy Code
    (gdb) dump binary memory flash_dump.bin 0x08000000 0x08080000
    • In OpenOCD use dump_image {FILE} {START} {LENGTH}. E.g., for STM32:
    Copy Code
    $ openocd -f interface/stlink-v2.cfg -f target/stm32f2x.cfg \
    -c "init" -c "reset halt" \
    -c "dump_image flash_dump.bin 0x08000000 0x80000" \
    -c "shutdown"

    Now we need to do a little reverse engineering to locate our desired variable somewhere inside that binary. Here are a few ways you might do that:

    • Is it possible the flags were stored as strings? Use the strings tool to print out all ASCII strings inside a binary file. Ex:
    Copy Code
    $ strings hardware/stm32/build/car_1234/flash_dump.bin
    ...
    FpGdefault_unlock
    default_feature1
    default_feature2
    default_feature3
    ...
    • Do the flags all share a common sequence, like starting with "FLAG{" or "default_"? Use hexdump and grep to locate that sequence. Ex:
    Copy Code
    $ hexdump -C flash_dump.bin | grep default_
    00060000  64 65 66 61 75 6c 74 5f  75 6e 6c 6f 63 6b 00 00  |default_unlock..|
    00060040  64 65 66 61 75 6c 74 5f  66 65 61 74 75 72 65 31  |default_feature1|
    00060080  64 65 66 61 75 6c 74 5f  66 65 61 74 75 72 65 32  |default_feature2|
    000600c0  64 65 66 61 75 6c 74 5f  66 65 61 74 75 72 65 33  |default_feature3|
    • As a last resort, you can load the flash dump into a decompiling tool like Ghidra to try to reconstruct the source code. Tools like that will often let you search for strings and string literals (like in the two bullets above) and also can point you to the memory address of your desired variable, assuming you can locate the right spot in the code where the variable is referenced. Ghidra is a powerful tool, though, and any further description of its use will have to wait for another article!

    We can summarize these attack steps in the following flowchart.

    Image of How Hardware Gets Hacked (Part 4): Memory Protections

    Other chips and their defenses

    Disabling the debug port and offering some kind of flash read protection are just two of many, many different ways that manufacturers can build security directly into the microcontroller's silicon. For protecting flash contents, other features can include:

    • Flash encryption: Flash is encrypted at rest and only decoded during the actual instruction fetching/decoding. If an attacker reads flash, all they get is ciphertext. (Ex: Espressif ESP32, Renesas RA6/RA4, NXP LPC55S, ST STM32U5/H5)
    • Debug authentication: The debug port can be locked with a password or certificate. Access is denied unless a debug adapter (presumably from an authorized developer or maintenance personnel) provides the correct password/certificate. (Ex: ST Micro STM32H5/U5, Nordic nRF53, Silicon Labs Series 2)
    • Tamper detection (with secure key storage): "Tamper" pins on the microcontroller can be connected to a grounded enclosure such that their connection breaks open if the enclosure is opened, causing a small, designated area of memory to get immediately erased. (Ex: ST Micro STM32U5, NXP LPC55S)

    Additionally, the following features have other useful security features that may be useful when we discuss more sophisticated attacks later on in this series.

    • Secure boot: A ROM bootloader verifies the cryptographic signature of flash memory before executing code. Can prevent an attacker from modifying flash memory or uploading their own program and executing the modified code. (Ex: ST Micro STM32H5/U5/L5, Microchip SAM L11, Renesas RA6/RA4)
    • Anti-rollback counters: Prevents loading older firmware versions. (Ex: NXP LPC55, Nordic nRF53)
    • Physically unclonable functions (PUF): Hardware circuits that create unique cryptographic keys from tiny random variations in IC manufacturing. (Ex: NXP LPC55S, Silicon Labs Series 2, Renesas RA6/RA4)

    Building a threat model

    With our first round of attack/defense under our belt, we can formalize our new knowledge in a threat model. A threat model is a graphic that depicts a device's security posture and includes things like:

    • who the expected attackers are
    • what is being protected (i.e., the payoffs)
    • which attacks a development team expects the attacker(s) to attempt
    • which defenses have been implemented to thwart those attacks
    • which attacks the team isn't protecting against

    The last bullet is often forgotten, but defining it is important to avoid over-engineering your system. For instance, disabling the debug port doesn't stop an attacker from decapping our microcontroller and then using a scanning electron microscope (SEM) to read out the bits of our flags one by one.

    Image of How Hardware Gets Hacked (Part 4): Memory Protections Peeling Back The Layers: How to Decap ICs and Spot Counterfeit Chips

    However, non-destructively decapping an integrated circuit and then reading out individual bits is a highly skilled and highly expensive endeavor. We may note on our threat model that this is a possibility, but that we've decided that if a nation state wants our secrets and is willing to sink that much money and effort into the process, then they can have them.

    There isn't a standard for drawing threat models, so we'll make our own.

    • Each rectangle will represent a version of our code, identified by a commit number.
    • Attacks will be shown as arrows, with a brief name and a list of flags that an attacker could capture when conducting the attack.
    • Attacks will point to updated versions of our code with any pertinent defenses in place.
    • The first time we encounter a defense, I'll also bring attention to the pitfalls in, and alternatives to, that defense.
    • Attacks considered outside our anticipated attackers' scope (and therefore not defended against) are marked by the words --OUT OF SCOPE--.

    Here's the threat model I developed after this first attack:

    πŸ‘€ Attackers

    • Teams of 4+ high-school and college students
    • Experience ranges from "hobbyist" to "graduating college senior, majoring in electrical engineering or cyber security"
    • Students are likely participating in the competition either as an extracurricular activity or a 1-3 credit-hour course (e.g., each student likely spends 1-12 hours a week on developing the project)
    • Teams are mentored by 1+ instructors, with experience ranging from "high school robotics or AP CS teacher" to "hardware security researcher"
    • Budgets are likely small, but have access to labs (especially at the college level) with equipment costing possibly tens of thousands of dollars each, such as

    πŸ” Secrets/Payoffs

    • The value of the car unlock and feature flags

    βš”οΈ Attacks (in- and out-of-scope) and πŸ›‘ Defenses

    Image of How Hardware Gets Hacked (Part 4): Memory Protections

    (Note: Navigate to the commit in question by appending the commit number to the URL www.github.com/nathancharlesjones/howHardwareGetsHacked/tree/<6-digit commit #>, i.e., www.github.com/nathancharlesjones/howHardwareGetsHacked/tree/d39462a)

    πŸ’‘ There, I fixed it

    Disabling the debug port doesn't just close off that attack path; it might actually open up new ones! Take a minute to brainstorm how an attacker might get around our defense or what other attacks might now be possible.

    πŸ” Hints:

    • Does this defense give the developers so much confidence that they're lax in their security of other parts of the system?
    • Devices often still need to be accessed for maintenance or to program configuration settings during production, even after being locked. Is there an interface like that that could be exploited?
    • Is there any way around the debug port being disabled?

    ☠️ One teeeeeny, tiiiny problem

    It turns out that disabling the debug port can definitely be circumvented! See these references for information about researchers who've partially or fully succeeded in circumventing RDP protections on the STM32.

    Some of these attacks are ones we'll probably see in future articles!

    Conclusion

    πŸ€” What do you want to remember?

    Think of at least 3 things you want to remember after reading this article, and write them in your notes.

    If nothing else, I'd ask you to remember this:

    • NEVER distribute plaintext binaries that contain system secrets
    • NEVER leave the debug port unlocked on a system that handles secrets

    Doing either one gives up those secrets to anybody who can use strings or gdb. But using the protections on each of our microcontrollers (and by virtue of the fact that binaries were encrypted during the competition) shuts down those easy attacks.

    So, what's our next attack??

    If you've made it this far, thanks for reading and happy hacking!

    Image of How Hardware Gets Hacked (Part 4): Memory Protections

    Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.