Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ jobs:
with:
release: "14.2.Rel1"

- name: Install CMake and Ninja
- name: Install building tools
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build
sudo apt-get install -y cmake ninja-build gcc-avr binutils-avr avr-libc

- name: Build Firmware
run: |
Expand All @@ -58,3 +58,4 @@ jobs:
uses: antmicro/renode-test-action@v5
with:
tests-to-run: firmware/tests/renode/src/*.robot
renode-revision: "v1.16.1"
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ build_simulation/
log.html
report.html
robot_output.xml

attiny.h

*-backups/
*.kicad_prl
Binary file added docs/images/glitch_screen_corrupted.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/glitch_screen_unstable.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/glitch_screen_verified.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 20 additions & 1 deletion docs/labs/02_i2c/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ Below is a check list of all the tools and skills that you need in order to perf

### Skill set


- Principles of electronics
- Basic familiarity with command line interfaces (CLIs)

## Objective

The goal of this lab is to exploit the I²C bus of the DVH board. Plain text communication with an EEPROM is performed to retrieve sensitive configuration variables such as passwords. Your task is to interact with I²C to gain root access to the UART shell to find the flags.

This lab contains 3 flags, formatted like so : `DVH{th1s_1s_4_f4k3_fl4g_385c951916}`.

## Instructions

Expand All @@ -42,3 +45,19 @@ Below is a check list of all the tools and skills that you need in order to perf
> This step is off-limits in the scope of the lab.

Since the EEPROM is independent from the STM32, it needs to go through an initialization process to make sure the environment is properly setup, and the EEPROM is in a valid state. In order to initialize or reset the lab, press the reset button while powering on the board. The LED will blink, and if everything goes smoothly, it will turn off. If it stays on, an error has occured, meaning the reset has not been completed. Please retry until you end up with an LED turned off.

### Initial access

Gain access to the UART shell as practiced in the [corresponding lab](../01_uart/README.md).

### User escalation

Now that you are an anonymous user inside the system, find a way to authenticate as a real system user.

> Hint : Where does the password live ? How is it transmitted ?

### Root escalation

With standard user privileges, find a way to gain root access to obtain the third flag.

> Hint : Exploit eveything in the environment, the EEPROM might respond to other masters than the STM32. Completing this step requires two specific clues.
46 changes: 46 additions & 0 deletions docs/labs/03_glitch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Voltage glitching

## Overview

Fault injection is a hardware hacking practice that aims to destabilize a device to bypass its security controls. Voltage glitching, or power glitching, is a common form of fault injection, where an attacker controls the power supply to trigger unstable behaviors.

When dropping the power supply to 0, the chip will obviously power off. However, when exposed to extremely fast (fraction of a millisecond) voltage drops at very precise timings (down to the CPU instruction), it is possible to perform a glitch that causes the CPU to skip its current instruction and continue its execution.

Skipping a single instruction can be particularly useful when encountering contidional branches (`if ... then ... else`). For example, if we have some sort of password check :

```c
if (user_input == expected_password) {
welcome()
} else {
get_out()
}
```

If we manage to skip the password verification using a voltage glitch, we would be able to call the `welcome()` function without knowing the password.

## Exploit

Voltage glitching is extremely powerful (and very hard to master). Because it is targeted at the silicon itself, any chip is virtually vulnerable to such attacks. Compromising the integrity of a system with voltage glitching may result to :

- Bypass password or security checks.
- Bypass potential Readout Protection measures.
- Obtain unexpected chip behavior, and potentially infinite possibilities.

The difficulty of power glitching resides in the strictness of finding a relevant width (to prevent the chip from passing out) and delay (to reliably attack the right instruction). Finding those typically requires a lot of trial and error, performing side-channel analysis (footprinting the chip to reliably detect when to trigger to glitch), and a bit of luck.

Some powerful tools specialized in glitching exist on the market, such as [ChipWhisperer](https://chipwhisperer.readthedocs.io/en/latest/getting-started.html) and the [PicoGlitcher](https://fault-injection-library.readthedocs.io/en/latest/overview/).

## Mitigations

There are common methods to protect a system against power glitching :

- Decoupling capacitors stabilize the power supply, making the chip sustain even with voltage drops. It is however possible for an attacker to desolder the chip.
- Brown Out Detectors (BOD) monitor the microcontroller's power and place the core in a safe state when the voltage falls below a certain level.
- Compare the values twice to harden the check itself.
- Add random time delays before sensitive operations for unpredictability.

Voltage glitching also suffers from the fact that it is a destructive practice that may damage, or permanently fry the chip. Depending on the target, it may also be frustrating because of its volatile nature, CPU clocks may drift, and an exploit's success rate may vary depending on the setup.

## Practice

You can now gain hands-on experience with Voltage Glitching by completing the [lab](./instructions.md) for this section.
Binary file added docs/labs/03_glitch/assets/attiny.bin
Binary file not shown.
154 changes: 154 additions & 0 deletions docs/labs/03_glitch/assets/glitcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import machine
import rp2
import time
import sys

machine.freq(250_000_000)

# Hardware state machine (perform the actual glitch)
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def glitcher():
pull() # Pull DELAY
mov(y, osr)
pull() # Pull WIDTH
mov(x, osr)

# Wait for TRIGGER to go HIGH
wait(0, pin, 0)
wait(1, pin, 0)

# Wait DELAY cycles
label("delay_loop")
jmp(y_dec, "delay_loop")

# Pull ATTiny VCC to GND for WIDTH cycles
set(pins, 1)
label("width_loop")
jmp(x_dec, "width_loop")

set(pins, 0) # Release MOSFET

mosfet_pin = machine.Pin(13, machine.Pin.OUT)
trigger_pin = machine.Pin(17, machine.Pin.IN, machine.Pin.PULL_DOWN)
sm = rp2.StateMachine(0, glitcher, freq=250_000_000, in_base=trigger_pin, set_base=mosfet_pin)

delay_cycles = 0
width_cycles = 0

print("Welcome to DVH glitcher")
print("Commands: 'fire', 'test trigger', 'set delay <val>', 'set width <val>'")

while True:
cmd = sys.stdin.readline().strip().lower().split()
if not cmd:
continue

if cmd[0] == "set" and len(cmd) == 3:
if cmd[1] == "delay":
delay_cycles = int(cmd[2])
print(f"[+] Delay set to {delay_cycles} cycles")
elif cmd[1] == "width":
width_cycles = int(cmd[2])
print(f"[+] Width set to {width_cycles} cycles")

elif cmd[0] == "test" and len(cmd) == 2 and cmd[1] == "trigger":
print("[+] Listening on TRIGGER pin...")

trigger_caught = [False]
def trigger_callback(pin):
trigger_caught[0] = True

trigger_pin.irq(trigger=machine.Pin.IRQ_RISING, handler=trigger_callback)
while not trigger_caught[0]:
pass

trigger_pin.irq(handler=None)
print("[!] Successfully captured TRIGGER signal")

elif cmd[0] == "fire":
print(f"[+] Launching glitch (delay: {delay_cycles}, width: {width_cycles})")
# Start state machine
sm.active(0)
sm.put(delay_cycles)
sm.put(width_cycles)
sm.active(1)

elif cmd[0] == "autosweep" and len(cmd) == 3:
start_delay = int(cmd[1])
step = int(cmd[2])
current_delay = start_delay

print(f"[+] Starting auto-sweep (initial delay: {current_delay}, step: {step}, width: {width_cycles}. Press CTRL+C to stop.")

try:
while True:
sm.active(0)
sm.put(current_delay)
sm.put(width_cycles)
sm.active(1)

if current_delay % 100 == 0:
print(f"[+] Scanning delay: {current_delay}")

timeout_start = time.ticks_ms()
triggered = False

while time.ticks_diff(time.ticks_ms(), timeout_start) < 8000:
if trigger_pin.value() == 1:
triggered = True
break

if not triggered:
print("[!] Timed out (TRIGGER is not rising)")
while trigger_pin.value() == 0:
pass
while trigger_pin.value() == 1:
pass

current_delay += step

except KeyboardInterrupt:
print("\n[-] auto-sweep stopped.")

elif cmd[0] == "autosweep2d" and len(cmd) == 5:
# autosweep2d <start_delay> <step> <min_width> <max_width>
start_delay = int(cmd[1])
step = int(cmd[2])
min_width = int(cmd[3])
max_width = int(cmd[4])
current_delay = start_delay

print(f"[+] Starting auto-sweep 2D (delay: {start_delay}, step: {step}, width Range: {min_width} to {max_width}). Press CTRL+C to stop.")

try:
while True:
# Sweep the widths for this delay
for current_width in range(min_width, max_width + 1):
sm.active(0)
sm.put(current_delay)
sm.put(current_width)
sm.active(1)

timeout_start = time.ticks_ms()
triggered = False
while time.ticks_diff(time.ticks_ms(), timeout_start) < 8000:
if trigger_pin.value() == 1:
triggered = True
break

if not triggered:
print("[!] Timed out (TRIGGER is not rising)")
while trigger_pin.value() == 0:
pass
while trigger_pin.value() == 1:
pass

if current_delay % 50 == 0:
print(f"[+] Scanned delay: {current_delay} (widths {min_width}-{max_width})")
current_delay += step

except KeyboardInterrupt:
print("\n[*] 2D AutoSweep stopped by user.")

else:
print("[!] Unknown command.")
65 changes: 65 additions & 0 deletions docs/labs/03_glitch/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Voltage Glitching lab instructions

## Overview

Here are the top-level instructions required to perform the Voltage Glitching lab. They are meant to give you the necessary information to get you started by yourself. If you ever feel stuck or need any help, you can read the lab's complete [walkthrough](./walkthrough.md) that will provide step-by-step instructions.

## Requirements

Below is a check list of all the tools and skills that you need in order to perform the lab. Everything is mandatory unless stated otherwise, except the skill set items. It is absolutely not a problem if you do not have some of the listed skills, but fundamentals of these topics is always nice to have. Anyway, you will learn more about those in the context of the lab. Feel free to do your own research whenever you lack understanding of any of the topics.

### Materials and equipment

- Computer
- DVH board with latest firmware (if not, you will need to [flash](../../tools/flash.md) it)
- Glitcher, we will use a Pico with [MicroPython](../../tools/micropython.md) for this lab
- Oscilloscope (optional)
- Micro-USB cable and 5-12V power supply
- Micro-USB to USB/USB-C cable (depending on available ports)
- Dupont cables (recommended) or regular wires
- 5.1K ohm resistor (tested) or any other value ranging from 1K to 10K

### Software

- Linux Physical or Virtual Machine (recommended)
- [MicroPython](../../tools/micropython.md) development environment

### Skill set

- Principles of electronics
- Some knowledge of MicroPython and the Raspberry Pi Pico
- Basic familiarity with oscilloscopes

## Objective

The goal of this lab is to exploit the ATTiny of the DVH board. A security check is performed on the ATTiny to verify the system's integrity, but unfortunately, it never passes. Your task is to perform a voltage glitching attack on the ATTiny to bypass the security check and obtain the flags.

This lab contains 2 flags, formatted like so : `DVH{th1s_1s_4_f4k3_fl4g_385c951916}`.

## Instructions

### Reset the lab

> This step is off-limits in the scope of the lab.

Since the ATTiny is independent from the STM32, it needs to go through an initialization process to make sure the environment is properly setup, and the ATTiny is in a valid state. In order to initialize or reset the lab, press the reset button while powering on the board. The LED will blink, and if everything goes smoothly, it will turn off. If it stays on, an error has occured, meaning the reset has not been completed. Please retry until you end up with an LED turned off.

### Dump the firmware

> Because the difficulty of this lab is already higher than the others, the [firmware](./assets/attiny.bin) is provided. If you have time, it would be a good exercise to attempt the dump yourself.

Dump a copy of the firmware running on the ATTiny to understand where the security check lives and how to detect it.

> Hint : Is there a specific trigger signal that is transmitted right before the security check ?

### Set up

Find the right spots on the board to perform the glitch. You will need the trigger from last step, and some sort of method to pull the voltage down.

> Hint : Is there a specific component near the ATTiny's power supply that can help drop the voltage easily ?

### Perform glitch

Find suitable parameters to bypass the security check by glitching the ATTiny. You can use this [MicroPython script](./assets/glitcher.py) to configure the parameters and drop the voltage.

> Hint : Try to interpret the outputs. What happens when you drop the voltage too long ? What if you do not drop it long enough ? Also, the `autosweep` and `autosweep2d` functions may help with scanning ranges automatically.
56 changes: 56 additions & 0 deletions docs/labs/03_glitch/walkthrough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Voltage Glitching lab walkthrough

## Overview

> This walkthrough is still Work In Progress, and some steps need to be explained more in depth, but it is complete enough to give you a working solution.

This is a complete walkthrough of the Voltage Glitching lab, that is meant to guide you through every step of the way. You can read through this document to perform the lab, but for educational purposes, it is strongly suggested to use the [instructions](./instructions.md) as a main roadmap, and come back here whenever you feel stuck or need a sanity check.

> By this point, I assume that you have read through the instructions and understood the goal of this lab.

## Walkthrough

### Dump the firmware

When starting this lab, the first thing we notice is the screen going crazy with "CORRUPTED" messages :

<p align="center">
<img width="500" src="../../images/glitch_screen_corrupted.jpg" alt="Glitch screen corrupted" />
</p>

By examining the [ATTiny firmware](./assets/attiny.bin), we notice that right before performing the check, it methodically raises its `PB4` pin. That is the trigger we can attach to.

### Set up

When looking at the PCB, we can see that the `VCC` pin is connected to a MOSFET transistor. Even better, there is a pin header that is linked to that MOSFET, which allows us to drop the ATTiny's power supply when that pin header goes high.

We can thus place two wires going from the Pico :

<div align="center">

| **DVH** | **Pico** | **Note** |
|:----------------:|:--------:|:----------------------------------:|
| ATTiny_PB4 / TP3 | GP17 | / |
| J8_left | GP13 | Place a 5.1K (or similar) resistor |

</div>

### Perform glitch

Using the [Glitcher script](./assets/glitcher.py), we can fire the MOSFET pin whenever the TRIGGER pin goes high, after a delay that we can calculate based on the number of CPU cycles performed before the instruction we want to bypass.

The width can be found by trial and error. The idea is to find a width that is at the edge between "too wide", which crashes the ATTiny all the time, and "too narrow", which does not induce faults. When the ATTiny crashes sometimes, but not too often, 50-50 for example, the width is a sweet spot to attempt glitching.

The first time the ATTiny browns out, the first flag will be printed on the screen along the "UNSTABLE" mention :

<p align="center">
<img width="500" src="../../images/glitch_screen_unstable.jpg" alt="Glitch screen unstable" />
</p>

When you finally manage to perform the glitch and skip the correct CPU instruction, the system will be marked as "VERIFIED", and the second flag will be printed :

<p align="center">
<img width="500" src="../../images/glitch_screen_verified.jpg" alt="Glitch screen verified" />
</p>

> The parameters that specifically worked for me are : 5.1K ohm resistor, width 328, delay 110450. A good corresponding command for the glitcher script would be `autosweep2d 105000 10 323 333`.
Loading
Loading