Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Physical Attacks Hardware Lab

Exploiting an insecure memcmp implementation on a real microcontroller to leak a secret.

esp32 picture

Image: Adafruit

Table of contents

  1. Background
    1. All attacks are fair game
    2. Requirements
  2. Prizes
  3. Dev Env Setup
    1. Windows Only- Install PuTTY and FTDI Drivers
  4. Victim
    1. Trying Passwords
    2. memcmp
  5. Serial
    1. Finding the port name
    2. Connecting to Serial
    3. Diagnosing Connection Problems
  6. Experiment Setup
    1. Wiring
  7. Programming
    1. Useful methods
    2. How to Upload Programs
    3. Measuring and Communicating with the Victim
  8. Suggested Attack Strategy
    1. Provided Code
    2. Hints
  9. Image Credits

Background

ESP32 is a Wifi and Bluetooth enabled microcontroller built on the Xtensa architecture. We will be using two of them today. One is the victim (a password checker from which we will leak the correct password) and the other is the attacker board (which you fully control).

You will be writing an ESP32 program on the attacker board to leak the secret from the victim by exploiting a timing side channel.

All attacks are fair game

  • You can do whatever you want to these boards.
  • Please try to leave them at least functional though for next year.
  • Our intended bug is probably the easiest way to exploit them but feel free to do whatever and explore!

Requirements

Students will bring:

  • A USB-A capable laptop setup (that is, bring your USB-C to USB-A converters!)
  • The Arduinio toolchain installed (see Dev Env Setup)

TA will bring:

  • 2x ESP32 boards, one programmed with the victim code, and one for the students to write attacker code on
  • 2x USB-A to USB-Micro B cables
  • A bundle of jumpers for students to use as needed

Prizes

The first 3 teams to finish will get 1 ESP32 board per group member (the same kind we used in class today).

Dev Env Setup

You will need to install the appropriate board programmer files to your local PC to program the attacker microcontroller with your attack.

This is the esp32 board we are using. It identifies in Arduino as an esp32/Node32s.

Here is the installation doc for esp32. A brief summary of what you need to do:

  1. Download the Arduino IDE 2.0.4
  2. Open Preferences
  3. Add https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to Additional Boards Manager URLs
  4. Close and reopen the Arduino app
  5. Open Boards Manager from Tools > Board > Board Manager…
  6. Search esp32
  7. Install esp32 Espressif Systems
  8. Close and Reopen the Arduino app
  9. Make sure Tools > Board is set to ESP32 Arduino/Node32s

Windows Only- Install PuTTY and FTDI Drivers

Install and configure PuTTY and the FTDI Drivers as seen here.

You’re all set to develop for the ESP32 now!

Victim

The victim board reads passwords over serial (more about that later- you can think of it as a command line for hardware devices), and tells you if the password was correct or not. Your job is to determine the correct password!

Here is what the victim board prints over serial:

                              ____    ____  _____  _________             
                             |_   \  /   _||_   _||  _   _  |            
                   ______      |   \/   |    | |  |_/ | | \_|    ______  
                  |______|     | |\  /| |    | |      | |       |______| 
                              _| |_\/_| |_  _| |_    _| |_               
                             |_____||_____||_____|  |_____|              
                                                                         
  ___                        _  _             _                       ___         _
 / __| ___ __ _  _ _ _ ___  | || |__ _ _ _ __| |_ __ ____ _ _ _ ___  |   \ ___ __(_)__ _ _ _
 \__ \/ -_) _| || | '_/ -_) | __ / _` | '_/ _` \ V  V / _` | '_/ -_) | |) / -_|_-< / _` | ' \
 |___/\___\__|\_,_|_| \___| |_||_\__,_|_| \__,_|\_/\_/\__,_|_| \___| |___/\___/__/_\__, |_||_|
                                                                                   |___/
Ravi's Super-Secure Password Checker(TM)
Enter your Password >

When the Super-Secure Password Checker first prototype was shown to customers, they didn’t like the fact that you cannot tell when passwords are being checked. So, we added a light that turns on while the password is being checked, and turns off afterwords.

Trying Passwords

You can type in test passwords and it will tell you if you are correct or not. Here’s an example where the correct password is MIT{this_is_a_secret}:

Enter your Password > MIT{incorrect_guess}
Try again.
Enter your Password > MIT{this_is_a_secret}
Correct!
Enter your Password >

You will notice that the blue light on the victim turns on when you try to check a password. The vendors added this light to provide feedback to the user that computation is going on internally. However, this light inadvertently leaks the time the MCU takes to check the password.

memcmp

Internally, the microcontroller is using a vulnerable definition of memcmp to compare your input against the password. Here is the memcmp definition and invocation:

/*
 * memcmp_unsafe
 * Compares buffers buf1 and buf2.
 * Returns true if they are exactly equal, false otherwise.
 */
bool memcmp_unsafe(uint8_t *buf1, uint8_t *buf2, size_t len) {
	for (size_t i = 0; i < len; i++) {
		if (buf1[i] != buf2[i]) {
			return false;
		}
	}
	return true;
}

/*
 * check_passwords
 * Checks the global user input buffer against the global secret.
 */
bool check_passwords(void) {
	turn_on_light(); // The users complained that there was no feedback, so we added a light
	bool result = memcmp_unsafe((uint8_t *)userbuf, (uint8_t *)SECRET, strlen(SECRET));
	turn_off_light();
	return result;
}

Notice that memcmp will quit early when it detects an incorrect character in your input. This means that passwords that contain more correct characters will take slightly longer to check than those with less correct characters at the beginning.

Serial

So, how do we actually communicate with the victim?

Serial, or UART, is an asynchronous protocol commonly used for communicating data between devices. As we saw in the earlier recitations, many consumer devices ship with exposed UART pads that actually transmit and receive data, which can cause many physical security vulnerabilities.

Serial consists of 3 pins- RX, TX, and ground. Ground is the same for both devices (as a general rule of thumb, ground is always common amongst everything in a system). One device’s RX connects to the other’s TX, and vice versa. RX is used for receiving, and TX is used for transmitting.

The baud rate is the speed at which serial communication runs. Our boards are configured to use the standard baud rate of 115200.

Let’s first test the victim serial connection to make sure everything’s working.

Finding the port name

  1. Open the Arduino app with no boards connected.
  2. Go to Tools > Port and look at the available ports.
    1. On macOS / Linux you will see something like /dev/cu.Bluetooth-Incoming-Port (or maybe nothing).
    2. On Windows you will probably see nothing, maybe a few COMX ports (X is a number).
  3. Plug in the victim board and reload Tools > Port and look for the new port.
    1. On macOS / Linux it will be a dev file like /dev/cu.usbserial-0001/.
    2. On Windows it will be something like COM4.
  4. Make note of the port name so we can connect to it later.

Connecting to Serial

Let’s connect to the victim board and see what it’s doing! With the victim board attached to your computer,

  1. If you’re on macOS / Linux, we can use the screen command to talk to the victim.
    1. screen {name from above} 115200 will give you a terminal.
    2. Press control + A, then K, and then Y when you want to exit.
  2. If you’re on Windows, follow the PuTTY instructions linked above.
  3. At first your terminal will be blank. Press the EN button on the board to reset it.
  4. You should now see some terminal output. Try typing in a guess password and see if it works. You should see the light flash and a result printed to the console.

Diagnosing Connection Problems

This resource can help you if you are having serial issues/ not seeing the board show up as a serial device. You may need to install the FTDI drivers if you’re on Windows.

Establishing Serial Connections to ESP32

Now you should be all set to compile programs and upload them.

Experiment Setup

Let’s wire up the full setup so we can start our timing analysis.

Here’s a diagram of the setup we will build:

We are going to attach the attacker and victim together via their secondary serial ports (TX2 and RX2). Both MCUs can also attach to your laptop via USB, although only one of them really needs to be connected (so you can see the results of the attack). We will also attach the LED pin of the victim to the attacker so it can measure when the light turns on.

Wiring

Make the following connections:

  1. Attacker.TX2 to Victim.RX2.
  2. Attacker.RX2 to Victim.TX2.
  3. Attacker.D2 to Victim.D2 (this is the LED pin).
  4. Attacker.GND to Victim.GND.
  5. Attach the attacker USB port to your computer.
  6. Optionally, attach the victim USB port to your computer. This is so we can passively monitor the victim and see what passwords the attacker is guessing.

Programming

Let’s learn how to write and upload programs to the attacker board.

Below is an example “Hello World” program for ESP32.

void setup() {
	// Runs once
	Serial.begin(115200);
}

void loop() {
	// Runs forever
	Serial.println("Hello, 6.888!");
}

The setup method runs once when the board is reset. The loop method runs forever after setup completes.

The provided attacker code handles setting up both serial to the computer, serial to the victim, and configuring the GPIO pins.

Useful methods

Here is a laundry list of useful methods you may want to use.

  • uint64_t esp_timer_get_time(): Reads from the processor high resolution timer register. Similar to the rdtsc instruction on x86.
  • Serial.println(anything): Print this thing to the serial port. Can handle numbers, char arrays, Strings, etc. just like on Arduino.
  • digitalRead(pin number): Returns the state of a given input pin (either HIGH or LOW).
  • delay(time in ms): Delay for some number of milliseconds. Useful for syncing up between the two devices/ allowing transfers to finish.

Arduino String Reference

Arduino Serial Reference

We provide methods for communicating with the victim and with the PC (send_to_computer, send_to_victim, read_byte_from_computer, and read_byte_from_victim). These are just wrappers around the excellent Arduino libraries.

Serial talks to the computer.

Serial2 talks to the victim.

How to Upload Programs

  1. Attach the microcontroller
  2. Ensure the correct port (don’t accidentally reflash the victim!) and board (Node32s) are selected in Tools > Port and Tools > Board respectively.
    1. Note that port are assigned by order of connection to the laptop, so the port name may change if you unplugged the boards and replugged them in later.
    2. To be safe you can make it a rule to always flash while only the attacker board is plugged in.
  3. Press the Upload button (it’s rightward pointing arrow). Upload Button
  4. Watch the output window at the bottom– if there are any errors, let me (your TA) know.
    1. You may see the output is stuck doing Connecting........_____....._____....._____. forever. If you see that, try holding down the BOOT button and trying again.

Take care to only program the attacker board. The victim board should not be reprogrammed ever, otherwise the secret will be lost!

Measuring and Communicating with the Victim

When we hooked the boards up in the wiring part of the experiment setup, we connected the victim’s LED pin to an attacker input pin (pin D2). So, by calling digitalRead(2), we can see what the status of the victim light is. It will either be HIGH or LOW (macros defined for you to use by the Arduino toolchain).

You can use the esp_timer_get_time method to read the current timestamp counter. By timing how long the light is on, you can time how long the victim spends in memcmp.

Use Serial2 to talk to the victim. You can use our wrapper methods send_to_victim and read_byte_from_victim or just use the Serial2 device with the Serial API.

Suggested Attack Strategy

  1. Start by determining how to time an individual victim memcmp (How do you know when it starts? How do you know when it ends?)
    1. For simplicity, you can start by only timing guesses issued manually via USB connection to the victim. (AKA type into the victim terminal yourself and have the attacker watch for the light).
    2. Then move onto having the attacker itself issue guesses.
  2. Move onto parsing the reply from the victim. (Did it say Correct! or did it say Try again. ? Do you care…?)
  3. Now combine the two to leak the entire secret…

Provided Code

  • You will fill in run_attacker just like in the spectre lab. (You have started the spectre lab, haven’t you? :D)
  • The provided code will take care of guess generation, you take care of determining which guess was best.
  • You will implement do_guess which issues one guess and returns the time it took.
  • You will then handle cleanup and state synchronization so that the victim and attacker stay in sync.
    • We provide sync_victim that may help with this.
    • Be careful not to deadlock.

Hints

  • If you are sending data too fast, the victim may not see it. Adding some delay()s while testing and then removing them later can’t hurt.
  • You can use Serial2.availableForWrite() to check if the write buffer is busy. This might be able to help you stop sending data too fast.
  • Take a look at the Arduino serial API, it helps a lot with implementing a serial protocol.
  • Valid solutions have a 100% success rate and mine takes 1 minute 30 seconds to run.

Image Credits

Apple, Adafruit, Espressif, Mouser, Bhphoto