Wednesday, January 29, 2025

Successful Fault Injection (glitching) with the Bus Pirate


Introduction

In this post, I'll discuss using power fault injection (glitching) to bypass UART password authentication in an application running on a simple Arduino dev board using the Bus Pirate.  (Spoiler - it works!)

The Bus Pirate is an open source hardware/firmware debugging and test tool that is capable of many, many things useful to an embedded engineer and/or hacker.  In this case, we'll be using the UART functionality to communicate with and time, generate, and inject a power fault into our target (an Arduino Uno).

Background

For a recent project, I was connected to a consumer IoT device's UART port and found that I could break into the U-Boot bootloader at which time I prompted to enter a password.  I noticed that once the password prompt mode was entered, it would allow infinite retries of password until the correct password was entered (not a good idea from a product security standpoint).  I later pulled the entire firmware from the flash of the device and reverse engineered the vendor's implementation of password entry in U-Boot.

This gave me the idea to use that experience to create a more simple, affordable, and easily understood introduction/example of power glitching.

Bus Pirate Code and Custom Glitching Circuit

The basic sequence that I wanted was:
  1. Send an '\r' (return) character from the Bus Pirate over UART to the target
  2. Wait a precise amount of time after the UART line goes back high from step 1
  3. Turn on one of the outputs from the Bus Pirate for a very precise and short amount of time
  4. Read the response from the target over UART - analyze that response to see if it looks different than the normal "### Please enter password ###" prompt.  If the response is different, stop and announce that the glitch was successful; otherwise, jump back to step 1.
This was easy to do as the Bus Pirate has open source firmware.  The microcontroller inside of the BP5 (Bus Pirate 5) is the Raspberry Pi RP2040 - this MCU has the PIO feature which allows for simple hardware based state machines.  One of these PIO state machines was used to control the timing needed in step 2 and 3 above.  All of this functionality was put together to create the "glitch" command for use in the BP5's UART mode.

The "glitch" command has been pulled into the main Bus Pirate firmware and can be found in the main branch of the project's github.  The three files that do most of the work are glitch.c, glitch.h, and glitch.pio.

A useful feature of the Bus Pirate is the "plank" accessory.  These are just small circuit boards that can be plugged into the BP's connector to perform a specific function or to interface to something.  One of these planks is the "blank plank"; basically a prototyping area which allows one to make whatever circuit they would like.

I used the blank plank to make a glitching plank:


(I had to rework the plank a few times, so the soldering is a little messy)

A rough schematic of my glitching plank:


I used the "Pin headers for measurements" to connect a scope across the FET to see the actual waveform of the glitch.  The "Pin header for logic analyzer" was used to help time the glitch.

What would I change about it?  The next version will have a small resistor in series with the FET's source; I've burned out a couple of FETs already from having the glitch "on" time too long.

The Target

As noted earlier, the target is an Arduino Uno development board.  This target was chosen because of low cost and wide availability.  I had two Sparkfun RedBoards in my parts drawer to use.

This board uses the Atmel ATMega32p microcontroller - an 8-bit RISC Harvard architecture chip with 32K of internal flash and 2K of internal SRAM running at 16MHz.  Programming can be done with the Arduino IDE and downloaded using USB.

To begin with, the schematic was reference to find a place to make a connection on the power circuit to inject the fault:


For best results, the point to do this should be very physically near the chip, so the usual place to connect to "short" the incoming power is across a decoupling capacitor near the microcontroller itself.  According to the schematic, the only decoupling capacitor appears to be C2, and it is not near the chip.

The 5VDC supply to the chip (VCC) comes in on pins 4 and 6, with pins 3 and 5 being ground.  Short wires were soldered on to pins 3 and 4 which then connected to an SMA coax connector:


The SMA connector was affixed to the board with hot-melt glue.  The SMA connector was used so a short coax cable could connect the target to the glitching hardware - this reduces unwanted parasitic capacitance or inductance in that connection.

Target Firmware

As stated earlier in this post, I was working on a consumer IoT device that had custom password verification added to U-Boot.  I eventually extracted the bootloader and reversed engineered that password code.  

That password verification code was included in the target's Arduino sketch:

int simpleCLILoop(const char* prompt) {

char password[MAX_PWD_LEN] = {'\0'};
uint8_t pwdInx;
int readChar;
int compareResult;

// if password has been entered (consoleEnabled == true), then
// skip this whole block. Otherwise keep trying until correct
// password has been entered.
while (!consoleEnabled) {
Serial.println("### Please enter password ###\r\n"); // prompt user
pwdInx = 0; // entered password character counter

// loop runs continuously until <RETURN> key pressed
while (true) {
if (Serial.available() > 0) { // wait for UART rx
readChar = Serial.read(); // get that char
readChar &= 0xff; // mask off lower 8 bits

if (readChar == '\r') { // user hit <RETURN>
break; // so break out of forever while() loop
} else if (readChar == '\b') { // user hit <BACKSPACE>
if (pwdInx > 0) { // if at least one char has been entered
--pwdInx; // decrement entered password char count
Serial.print("\b \b"); // send backspace, space, backspace over UART
} // to clear the last '*'
} else { // not <RETURN> or <BACKSPACE>
password[pwdInx] = readChar; // append the this char to the user's password entry
++pwdInx; // increment the entered password char count
Serial.print('*'); // print out an asterix
} // testing entered UART char
} // waiting for UART char
} // main while() loop

password[pwdInx] = '\0'; // null terminate user entered password

// compare user entry to "good" password
compareResult = strcmp(PASSWORD, password);

if (!compareResult) {
consoleEnabled = true; // set bit to indicate password is good!
}

Serial.println(""); // print out a newline
} // while (!consoleEnabled)

*consoleBuffer = '\0';
return (readCommandFromUART(prompt));
}

(Yes, I know there's a stack-based buffer overflow here, and that it's a bit sketchy in general, but this is the code I pulled from a real-world consumer IoT device.  The only difference between the consumer device and this is that I'm being lazy and using the Arduino "Serial" class instead of directly reading/writing to the ATMega 328's "UDR0" register.)

The code to target here is:

if (!compareResult) {
consoleEnabled = true; // set bit to indicate password is good!
}

If we can inject a fault at the right time, the code could skip over the if() statement and assign a "true" to the global "consoleEnabled" variable.

For even more detail, the disassembly from the region near the password check looks like this:

    code:02f4 91 e0           ldi      R25,0x1
    code:02f5 0e 94 be 03     call     strcmp                               

    code:02f7 89 2b           or       R24,R25
    code:02f8 11 f4           brbc     LAB_code_02fb,Zflg
    code:02f9 a0 92 d4 02     sts      consoleEnabled,R10  
                      LAB_code_02fb                         XREF[1]:  code:02f8(j)  
    code:02fb 85 ea           ldi      R24,0xa5
    code:02fc 91 e0           ldi      R25,0x1

This line:

code:02f5 0e 94 be 03     call     strcmp 

calls strcmp(), the result is returned as a 16-bit integer in registers R24 and R25.  The next line:

code:02f7 89 2b           or       R24,R25

performs a logical OR on those two registers.  Why do that when we are trying to compare the return value to zero?  The OR instruction for AVR will set the Z flag in the status register if the result of the OR is zero (all bits clear).  To compare a 16-bit integer to zero takes more than one instruction, so the compiler is smart enough to optimize down to the OR.  Following the OR is this line:

code:02f8 11 f4           brbc     LAB_code_02fb,Zflg

this is a branch instruction which will branch to the label LAB_code_02fb if the Z flag in the status register is cleared.  In other words, jump to that label if the password was incorrect.  If the Z flag was clear (strcmp() returned non-zero), the code will skip this next line:  

code:02f9 a0 92 d4 02     sts      consoleEnabled,R10

which sets the global "consoleEnabled" to be "true".  (The compiler reused register R10 from some previous thing here.)

Looking at that, the specific instruction to "glitch" or skip over would be the brbc branching instruction.  It's also possible to inject a fault into the code of the strcmp() routine so it returns zero, but this is where I am targeting.


Connecting Things Up

Here is the BP with glitching plank connected to the Arduino.  


In the upper right are the UART TX and RX signals from the Arduino  going to the BP.  Next is the coax cable connecting the two together; the +5VDC (VCC) right at the AVR chip across the FET.  The two wires from the lower header on the target connect the Arduino's 5V and gnd to the glitching plank.  The BP is using the target's voltage to power the I/O buffers.

The logic analyzer (not shown) would be connected to the 2-pin header for RX/TX for timing.

Finding the Right Time

As described above, the key is finding the right time to inject the fault.  I used a logic analyzer to help figure this out.  The analyzer was connected to the TX and RX lines and recorded the sequence of transmit from the BP and receive from the Arduino:


The top line shows the serial TX line from the BP sending a '\r' (return).  Then the lower line shows the Arduino responding with "### Please enter password ###".  The timing markers show that the time between the two is about 29 microseconds.  I ran this several times and found this timing to be fairly consistent.

For this attack, the fault should be injected at some point during the 29 microseconds after the BP does its transmission.  A guess would be at some point near the middle of this time.  It will take some trial and error to find the "ideal" time.  

The same applies to the duration of the injected fault.  Too long and it'll trip the brown out detector of the microcontroller and cause it to reset; too short and it won't have the desired effect.  Again, trial and error.

Performing the Fault Injection Attack

Now is the time to actually do the attack!  First, connect to the Bus Pirate's serial console and enable UART mode.  Once that's done, I issued the "bridge" command and powered up the Arduino:


(The Bus Pirate powers up in a safe High Impedance mode; the "m 3" command is shorthand for mode 3 - UART).  The bridge command sets the BP's UART into "transparent bridge", and shows the target is in password mode.  

Bridge command was exited with the BP's button and the "glitch" command was issued:

Breaking all of that down, first the glitch configuration information is shows (and can be edited):

  • Glitch trigger character: The ASCII value of what to send to trigger the whole sequence.  In this case, it's ASCII 13, a RETURN character.  
  • Glitch trigger delay:  How long after sending the trigger character to delay before turning on the output that fires the FET?  This value is in terms of nanoseconds * 10; so a value of 14 is 14,000 nanoseconds, or 14 microseconds.  That puts it in the window shown on the logic analyzer
  • Glitch vary time: This is an additive time to vary the glitching timing, again in nanoseconds * 10.  In this case, it's a 5; the actual glitches will be fired at 14.00us, 14.01us, 14.02us, 14.03us, 14.04us, and 14.05us.
  • Glitch output on time: How long to keep the FET gate turned on.  This should be a short time; too long and you can trip the brown out detect and reset the device, too short won't trigger a glitch
  • Glitch cycle delay: Minimum time between attempts; this is used to keep the FET or other hardware from overheating
  • Normal response character: The ASCII value of something returned by the target when operating normally.  In this case, it's an ASCII 80, which is a capital 'P'.  If the glitch did not trigger the bypass, the target will respond with "### Please enter password ###".  Therefore, if a capital "P" was RX'd by the UART, the code knows it didn't succeed
  • Number of glitch attempts: The number of cycles to attempt before giving up.  A cycle is a series of attempts including all variations based on the Glitch vary time.
  • Bypass 'READY' input checking: The glitching code is generic enough to be used in different ways; for example, to trigger an external device that may need to recharge between attempts.  To accommodate that, the glitching firmware can wait until an input has been turned on by that external device.  In this case, the FET-based plank doesn't need this, so it is bypassed.
Next we see this:

UART glitching.  Press Bus Pirate button to exit.
Attempt   1, delay 14000ns RX: ### Please enter pa
Attempt   1, delay 14010ns RX: ### Please enter pa
Attempt   1, delay 14020ns RX: ### Please enter pa
Attempt   1, delay 14030ns RX: ### Please enter pa
Attempt   1, delay 14040ns RX: ### Please enter pa
Attempt   1, delay 14050ns RX: $> 
Target glitch success!

On the 6th attempt, the BP was able to glitch past the password check on the Arduino!  The BP will output information for each attempt: the delay (calculated by the base plus the current amount of variation), and the data received from the target.

In the first 5 tries, the Arduino returned the message to enter password (the code only shows the first part of the response).  Those responses contained a capital "P", so it was obvious it didn't succeed.

However, the 6th response was "$>".  That didn't contain a "P", so it was declared a success!

After that, I re-entered the "bridge" command.

UART> bridge
UART bridge. Press Bus Pirate button to exit.

$> help
This is just a small example, there really isn't any kind of command
processing.  Only commands are 'help', 'reboot', and 'info.
$> info
Target info:
'consoleEnabled': true
$> 

This time, the UART showed an interactive user session with a "$>" prompt.  In the Arduino code, this is a very simple thing with only 3 commands to show that the user is past the password entry.  The "info" command outputs the current value of the global "consoleEnabled" flag, which is true.

Conclusion

This example showed that fault injection can be used to bypass simple authentication.  It didn't require expensive hardware or lab equipment.

Comment below if you try this or something like this, I'd love to see how it works for others!

References

Web references:

/******************************************************
* sample code to exercise glitching a 328p
*
* After powering up, the code will put out a short
* identifier, then start asking for a password. (over UART)
*
* User can try as many times as they want, there is
* no limit.
*
* If correct password is entered, the user will get a
* "$>" prompt.
*
* The goal is the inject a power or other fault just
* as the password is being checked and thereby bypass
* password checking.
*******************************************************/

#define MAX_PWD_LEN 32
#define CONSOLE_BUFFER_LEN 64

static const char* PASSWORD = "myP4ssw0rd";
static bool consoleEnabled = false;

char consoleBuffer[CONSOLE_BUFFER_LEN] = {'\0'};

int simpleCLILoop(const char* prompt);
int readCommandFromUART(const char* prompt);

void setup() {
Serial.begin(115200);
Serial.println("************************************\r\n Test glitch target (victim), v 0.9\r\n************************************");
}

void loop() {
// put your main code here, to run repeatedly:
simpleCLILoop("$> ");
}


/******************************************
* This method is called continually to get
* user interactivity over UART. If the
* password has not yet been entered, this
* method will continually poll for it
* until it is found.
*
* This method may seem strange, and even
* a bit contrived, but it is somethine that
* I pulled almost exactly from a consumer
* IoT device.
*
* Yes, there is a stack-based buffer overflow
* here (and is in the actual device out in
* the world, too.)
******************************************/
int simpleCLILoop(const char* prompt) {

char password[MAX_PWD_LEN] = {'\0'};
uint8_t pwdInx;
int readChar;
int compareResult;

// if password has been entered (consoleEnabled == true), then
// skip this whole block. Otherwise keep trying until correct
// password has been entered.
while (!consoleEnabled) {
Serial.println("### Please enter password ###\r\n"); // prompt user
pwdInx = 0; // entered password character counter

// loop runs continuously until <RETURN> key pressed
while (true) {
if (Serial.available() > 0) { // wait for UART rx
readChar = Serial.read(); // get that char
readChar &= 0xff; // mask off lower 8 bits

if (readChar == '\r') { // user hit <RETURN>
break; // so break out of forever while() loop
} else if (readChar == '\b') { // user hit <BACKSPACE>
if (pwdInx > 0) { // if at least one char has been entered
--pwdInx; // decrement entered password char count
Serial.print("\b \b"); // send backspace, space, backspace over UART
} // to clear the last '*'
} else { // not <RETURN> or <BACKSPACE>
password[pwdInx] = readChar; // append the this char to the user's password entry
++pwdInx; // increment the entered password char count
Serial.print('*'); // print out an asterix
} // testing entered UART char
} // waiting for UART char
} // main while() loop

password[pwdInx] = '\0'; // null terminate user entered password

// compare user entry to "good" password
compareResult = strcmp(PASSWORD, password);

if (!compareResult) {
consoleEnabled = true; // set bit to indicate password is good!
}

Serial.println(""); // print out a newline
} // while (!consoleEnabled)

*consoleBuffer = '\0';
return (readCommandFromUART(prompt));
}


/******************************************
* *
******************************************/
int readCommandFromUART(const char* prompt) {
bool enterd = false;
int readChar = 0;
int readInx = 0;

Serial.print(prompt);

while (!enterd) {
if (Serial.available() > 0) {
readChar = Serial.read();
readChar &= 0xff;

if (readChar == '\r') {
break;
} else if (readChar == '\b') {
if (readInx > 0) {
--readInx;
Serial.print("\b \b");
}
} else {
consoleBuffer[readInx] = readChar;
++readInx;
Serial.print((char)readChar);

// dont allow a buffer overflow here
if (readInx == (CONSOLE_BUFFER_LEN - 1)) {
break;
}
}
}
}

consoleBuffer[readInx] = '\0';
Serial.println("");

if (strlen(consoleBuffer)) {
if (!strcmp("info", consoleBuffer)) {
Serial.println("Target info:");
Serial.print("\t'consoleEnabled': ");
(consoleEnabled) ? Serial.println("true") : Serial.println("false");
} else if (!strcmp("help", consoleBuffer)) {
Serial.println("\tThis is just a small example, there really isn't any kind of command");
Serial.println("\tprocessing. Only commands are 'help', 'reboot', and 'info.");
} else if (!strcmp("reboot", consoleBuffer)) {
// kind of a sledgehammer - jump to the reset vector to reboot.
asm("jmp 0");
} else {
Serial.print("'");
Serial.print(consoleBuffer);
Serial.println("' isn't a supported command.");
}
}

return (strlen(consoleBuffer));
}
 

I'll be speaking at CypherCon!

 Just a short note here - I'll be speaking at CypherCon in early April.  My topic is about hacking a cheap IP camera, and what the vendor could have done to make it less hackable.


Hope to see you there!

Successful Fault Injection (glitching) with the Bus Pirate

Introduction In this post, I'll discuss using power fault injection (glitching) to bypass UART password authentication in an application...