0%

侧信道攻击伪实战-分析一道硬件CTF题

简介

开始

  • 将上一篇中构造的Arduino直接拿过来用,但有个地方需要稍作调整,将一个20Ω的电阻与给Atmega328P 供电的GND串联,用于给ChipWhisperer-Lite测电压

  • 为什么不连Vcc而连GND?因为实际使用中发现貌似无法避免ChipWhisperer-Lite GND与供电端的GND相连,这样相连的情况下把20Ω电阻接Vcc上,再用ChipWhisperer-Lite测电压,就会出现短路,这种情况貌似有个专业术语叫"Ground Loop"

  • 如下图,把ChipWhisperer-Lite当作电压表,把Atmega328P和其它器件整体看成一个大电阻,如果把20Ω接在Vcc端,并且ChipWhisperer-Lite又与Atmega328P共地的情况下,就会使Atmega328P被短路了。实测按下图接法,芯片无法正常工作且20Ω电阻将发烫。

    1

  • 因为找不到好的解决方法,于是改为20Ω电阻接在电源的GND出,更改后的电路示意图,如下图所示,这样就避免了将Atmega328P芯片短路,电阻也不会发烫

    image-20220903222608291

  • 下图为实际电路接线图,chipwhisperer-lite用一个sma转鳄鱼夹将一个20Ω来测电阻两端电压(试过用sma转bnc连接示波器的x1探头,但是效果不太行,可能是阻抗的问题)

    3

  • 下图为chipwhipserer-lite中引出来的线,这里只引出来了TIO4(chipwhipserer-lite正面朝上的情况下,20PIN下面的那排左数第3针脚即为TIO4),用于设置触发信号的探针

    4

  • 什么是TIO4?- 这得跟chipwhisperer的官方文档有关,chipwhisperer-lite主要有TIO1、TIO2、TIO3、TIO4这四个针脚可用于设置触发信号(其中TIO1和TIO2默认为Rx和Tx,TIO4默认为触发信号的输入),更多细节请看官方文档https://chipwhisperer.readthedocs.io/en/latest/scope-api.html

  • 如下图所示,chipwhisperer-lite的背面有TIO1、TIO2、TIO3、TIO4的定义,所以只有用万用表量一下,看这4个针脚跟20PIN座子的哪几个针脚相连即可知道,哪个是Rx、哪个是Tx和哪个是触发信号的输入

    5

  • 接下来为Atmega328p刷入硬件CTF的题目,如果搭建的最新系统可以自动Reset并正常通过USB转UART上传程序,那么可以用下面的命令上传(只需修改USB转UART设备)

    1
    avrdude  -v -c arduino -p atmega328p -P /dev/cu.wchusbserial1410 -b115200 -u -V -U flash:w:piece_of_scake.hex
  • 如果不能通过USB转UART上传程序,那么可以用上一篇的Arduino Uno当作ISP编程器,为Atmega328p烧录固件

    1
    avrdude -p m328p -P /dev/cu.usbmodem14201 -c avrisp -b 19200 -U flash:w:piece_of_scake.hex
  • 上传好固件后,就可以先来测试,这里使用python的pyserial包来使用串口

    1
    pip install pyserial
  • 跟据题目提示,使用"e"+ 16 bytes即可加密,

    image-20220903214212850

  • 如下图所示,设置好选用的设备名称和波特率即可用write和read方法来与芯片进行串口通信,可以看到使用"e"+16个0x01加密返回结果为"5cf473073f0a0940be290224ca495b90"。如果格式不对将没有任何响应

    image-20220904105456498

  • 实际使用过程中可以发现连接在Atmega329P的19引脚和22引脚的二极管会在正确发送指令后亮一下,因此可以用chipwhipserer的TIO4与Atmega329P19引脚相连来作为触发信号的输入

  • 设置好触发信号后,就可以开始写代码,用chipwhipserer-lite来抓取能量迹,正式开始之前,建议把面包板上的电容都去掉(因为电容有滤波效果,所以会影响抓取的能量迹波形)。而且建议给Arduino最小系统独立供电(串联的电阻较大时,靠笔记本供电,会电压不足),笔记本电脑拔掉充电头(电脑充电状态会有杂峰影响)

  • 接下来照猫画虎,将之前侧信道攻击学习的前面部分的代码复制过来,并稍作修改,如下图所示,这里还是使用USB转UART作为串口通信(虽然chipwhisperer-lite有Rx和Tx,但是chipwhisperer-lite是3.3v的,而atmega328p是5v的,所以两者不能直接通信)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import serial

    SCOPE="OPENADC"
    PLATFORM="CWLITEXMEGA"
    CRYPTO_TARGET="TINYAES128C"
    VERSION="HARDWARE"

    %run "../../Setup_Scripts/Setup_Generic.ipynb"

    s=serial.Serial('/dev/cu.wchusbserial1410', 19200, timeout=0.5)

    proj = cw.create_project("test", overwrite=True)

    image-20220904141218312

  • 接着设置chipwhisperer-lite的触发信号输入引脚,触发模式,采样频率等,具体可查看官方文档,下面的设置为使用TIO4引脚作为触发信号,单次采样5000个点的数据,还有采样频率"clkgen_x4"(如果为"clkgen_x1",那么将会用更低的频率采样)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # scope.adc.basic_mode = "falling_edge"
    # scope.adc.basic_mode = "high"

    scope.adc.samples = 5000
    scope.clock.adc_src = "clkgen_x4"

    scope.trigger.triggers = "tio4"

    scope.adc.offset=0

    scope.adc.presamples=0

    image-20220904141520510

  • 接着是,开始抓取能量迹,第一个key可以随意设置(主要用于后面画图高亮显示,如果一开始不知道真正key可以用16个00代替)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import chipwhisperer

    from tqdm import tnrange
    import numpy as np
    import time

    reset_target(scope)

    ktp = cw.ktp.Basic()
    key, text = ktp.next()
    # key=bytes([0]*16)
    key=bytes([0xaf, 0x23, 0xd5, 0x45, 0xa0, 0xea, 0xe6, 0xa0, 0x74, 0x65, 0x96, 0xca, 0xce, 0x51, 0xf0, 0xf7])
    target.simpleserial_write('k', key)
    trace_array = []
    textin_array = []
    textout_array=[]

    N = 500
    for i in tnrange(N, desc='Capturing traces'):

    scope.arm()
    s.write(b"e"+bytes(text))
    ret = scope.capture()
    r=s.read_all()
    while len(r)==0:
    r=s.read_all()

    if ret:
    print("Target timed out!")
    continue
    trace = scope.get_last_trace()

    print(r.hex())
    trace_array.append(trace)
    proj.traces.append(chipwhisperer.common.traces.Trace(trace, text, r,key))
    textin_array.append(text)
    key, text = ktp.next()
    proj.save()

    image-20220904141951653

  • 抓完能量迹后,可以话出一条能量迹,查看效果,如下图所示

    1
    2
    3
    4
    5
    6
    %matplotlib notebook
    import matplotlib.pylab as plt

    plt.figure()
    plt.plot(trace_array[0])
    plt.show()

    image-20220904142218949

  • 最后就是执行CPA攻击,可以看到最终结果为"0xaf, 0x23, 0xd5, 0x45, 0xa0, 0xea, 0xe6, 0xa0, 0x74, 0x65, 0x96, 0xca, 0xce, 0x51, 0xf0, 0xf7"

    1
    2
    3
    4
    5
    6
    7
    import chipwhisperer as cw
    import chipwhisperer.analyzer as cwa

    leak_model = cwa.leakage_models.sbox_output
    attack = cwa.cpa(proj, leak_model)
    cb = cwa.get_jupyter_callback(attack)
    results = attack.run(cb, 10)

    image-20220904142343167

  • 验证密钥是否正确,如下图所示,使用标准AES,用侧信道攻击得到的密钥,加密同样的数据[1]*16得到的结果同样为"5cf473073f0a0940be290224ca495b90",说明"0xaf, 0x23, 0xd5, 0x45, 0xa0, 0xea, 0xe6, 0xa0, 0x74, 0x65, 0x96, 0xca, 0xce, 0x51, 0xf0, 0xf7"是正确的AES密钥

    1
    2
    3
    4
    5
    6
    7
    from Crypto.Cipher import AES

    key=bytes([0xaf, 0x23, 0xd5, 0x45, 0xa0, 0xea, 0xe6, 0xa0, 0x74, 0x65, 0x96, 0xca, 0xce, 0x51, 0xf0, 0xf7])
    aes=AES.new(key, AES.MODE_ECB)

    data=bytes([1]*16)
    aes.encrypt(data).hex()

    image-20220904142623815

  • 至此成功利用侧信道攻击解决了这道CTF

其他

  • 如果不去掉电容会怎样?- 如下图,抓取的波形的振幅会减小,恢复正确的密钥需要的能量迹更多,去掉电容只需200条左右能量迹即可恢复正确密钥,但不去掉密钥则需要500条左右甚至更多

    image-20220904143319351

  • 为什么串联20Ω大小的阻值,其它大小可以吗?- 理论上可以,试过1Ω的电阻(但效果不行),试过50Ω(分压太明显,需要输入更多的电压,大概6.8v输入才能稳定正常工作,20Ω则只需5.5v输入)

总结

  • 利用侧信道攻击成功解了一道硬件CTF题
  • 通过自己构建电路,才知道实施一次侧信道攻击不是一件容易的事