
























通过 USB 直接连接电脑,可以同时满足 badge 的供电和串口通信,因此我们调节 Badge 开发板侧边开关拨至 RF1 而非 SW1 以设置板子为 USB 供电而非电池供电,然后通过 USB 串口以 38400 波特率连接上开发板,会看到如下的题目信息(如果使用电池供电,则会提示电压不足,不会显示题目信息):

开发板关键信息介绍:

我们只关注 AES 侧信道攻击获取密钥这个实验。由题目和开发板介绍,可知是希望通过侧信道/故障注入的方式恢复 AES 密钥,通过串口发送 'AES',芯片会进行 AES 加密运算,AES 加密过程中 PB11 作为触发信号会先拉高再拉低。实验以电压故障注入为例,打在 AES 第九轮运算的位置,产生足够多的错误密文,从而通过 DFA 恢复密钥。最后输入正确的 flag 即可成功点亮 PB0。

这张图展示的是电压故障注入攻击的硬件接线方案,核心设备是左侧的 PowerShorter。
整体攻击流程: 电脑通过串口触发 AES → PB11 输出触发信号 → PowerShorter 在精确时刻短路 3.3V 电源 → MCU 计算出错 → 电脑读取错误密文 → 与正确密文配对用于 DFA 分析。
restart() 函数。glitch_delay 延迟,发出持续 glitch_pulse 宽度的短路脉冲。ser.write(b'AES\n'))并读取加密结果。
通过示波器以 PB11 为触发信号,采集功耗波形可以大致看出 AES 第九轮运算时间在触发后 2.3ms 左右:

# 引入库
from power_shorter import *
import faultviz, time, random,serial
import numpy as np
from osrtoolkit.dfa import aes as dfaaes
# 初始化PowerShorter端口和串口端口
ps = PowerShorter('COM4')
ser = serial.Serial("COM19", 38400, timeout=0.5)
# 启动一个故障注入结果的实时可视化界面服务
faultviz.start_view_service(port=12345)
vt = faultviz.ViewWidget()
# 定义重启函数
def restart():
ps.relay(RELAY.RELAY2,1)
time.sleep(0.5)
ps.relay(RELAY.RELAY2,0)
time.sleep(0.5)
# 获取一次正常(无故障注入)的 AES 加密结果,作为后续 DFA 攻击的参考基准
ser.reset_input_buffer()
ser.write(b'AES\n')
c = ser.read(32)
c_correct = bytes.fromhex(c.decode())
c_correct
# 初始化一个空列表,用来收集所有满足 DFA 攻击条件的正确密文与故障密文配对
fault_list_pair = []
# 执行一次完整的电压毛刺故障注入尝试的函数
def STM32F303_attack():
glitch_pulse = random.randint(14, 18)
glitch_delay = random.randint(229500, 232500)
ps.engine_cfg(Engine.E1, [(0, glitch_delay), (1, glitch_pulse), (0, 1)])
ps.arm(Engine.E1)
time.sleep(0.5)
ser.reset_input_buffer()
ser.write(b'AES\n')
line = ser.read(32)
try:
c = bytes.fromhex(line.decode())
s = ps.state(Engine.E1)
if s == "glitched":
if dfaaes.DfaAESSubFunctions.check_fault_pair(c_correct, c) == 9:
status = "SUCCESS"
fault_list_pair.append((c_correct.hex(), c.hex()))
else:
status = "NORMAL"
vt.update(state = status, delay = glitch_delay, pulse = glitch_pulse, result = c.hex())
except:
restart()
# 无限循环地反复尝试故障注入,直到收集到足够多的有效故障对,实际上此实验只需20个左右的有效故障对
while(1):
STM32F303_attack()
# 展示故障注入结果的实时可视化界面
vt.show()
# 用收集到的故障对反推 AES 密钥
DFA_KEY = dfaaes.DfaAES(fault_list_pair)
DFA_KEY.get_master_key()
判断 SUCCESS 的依据:if dfaaes.DfaAESSubFunctions.check_fault_pair(c_correct, c) == 9
多次注入得到24个有效故障对:

用收集到的故障对反推 AES 密钥:

最后将恢复出来的密钥裹上 flag{} 得到 flag{3e0d16a0a614c45b423628799b22f3de},通过串口发送即可点亮 PB0:

此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。