Physical Attacks Hardware Lab
Exploiting an insecure memcmp
implementation on a real microcontroller to leak a secret.
Image: Adafruit
Table of contents
- Background
- Prizes
- Dev Env Setup
- Victim
- Serial
- Experiment Setup
- Programming
- Suggested Attack Strategy
- 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:
- Download the Arduino IDE 2.0.4
- Open Preferences
- Add
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
to Additional Boards Manager URLs - Close and reopen the Arduino app
- Open Boards Manager from Tools > Board > Board Manager…
- Search
esp32
- Install
esp32 Espressif Systems
- Close and Reopen the Arduino app
- 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
- Open the Arduino app with no boards connected.
- Go to Tools > Port and look at the available ports.
- On macOS / Linux you will see something like
/dev/cu.Bluetooth-Incoming-Port
(or maybe nothing). - On Windows you will probably see nothing, maybe a few
COMX
ports (X
is a number).
- On macOS / Linux you will see something like
- Plug in the victim board and reload Tools > Port and look for the new port.
- On macOS / Linux it will be a dev file like
/dev/cu.usbserial-0001/
. - On Windows it will be something like
COM4
.
- On macOS / Linux it will be a dev file like
- 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,
- If you’re on macOS / Linux, we can use the
screen
command to talk to the victim.screen {name from above} 115200
will give you a terminal.- Press
control + A
, thenK
, and thenY
when you want to exit.
- If you’re on Windows, follow the PuTTY instructions linked above.
- At first your terminal will be blank. Press the
EN
button on the board to reset it. - 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:
Attacker.TX2
toVictim.RX2
.Attacker.RX2
toVictim.TX2
.Attacker.D2
toVictim.D2
(this is the LED pin).Attacker.GND
toVictim.GND
.- Attach the attacker USB port to your computer.
- 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 therdtsc
instruction on x86.Serial.println(anything)
: Print this thing to the serial port. Can handle numbers,char
arrays,String
s, etc. just like on Arduino.digitalRead(pin number)
: Returns the state of a given input pin (eitherHIGH
orLOW
).delay(time in ms)
: Delay for some number of milliseconds. Useful for syncing up between the two devices/ allowing transfers to finish.
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
- Attach the microcontroller
- Ensure the correct port (don’t accidentally reflash the victim!) and board (
Node32s
) are selected in Tools > Port and Tools > Board respectively.- 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.
- To be safe you can make it a rule to always flash while only the attacker board is plugged in.
- Press the Upload button (it’s rightward pointing arrow).
- Watch the output window at the bottom– if there are any errors, let me (your TA) know.
- You may see the output is stuck doing
Connecting........_____....._____....._____.
forever. If you see that, try holding down theBOOT
button and trying again.
- You may see the output is stuck doing
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
- Start by determining how to time an individual victim
memcmp
(How do you know when it starts? How do you know when it ends?)- 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).
- Then move onto having the attacker itself issue guesses.
- Move onto parsing the reply from the victim. (Did it say
Correct!
or did it sayTry again.
? Do you care…?) - 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.
- We provide
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