简介
本篇将基于chipwhisperer-jupyter sca101的Lab 2_1B来学习如何通过能量分析得出简单密码验证系统的正确密码
开始
可以看到同样有MAIN、HARDWARE和SIMULATED,跟之前一样,这里选择把HARDWARE的代码复制到MAIN执行
开始同样是选择合适的平台,这里因为目标板是XMEGA,所以只需改PLATFORM为'CWLITEXMEGA'即可
前几行代码其实作用就是初始化一些参数和编译合适的目标板固件并上传至目标板
接着运行,可以看到'OK to continue!',说明前面的配置没有问题,可以继续往下运行
接着看到如下提示信息,大意是:“所以我们能做些什么呢?首先我要作弊并且告诉你我们拥有一个以'h'开头的预设密码,并且密码长度是5位,但是这是唯一的提示-你能用这些信息来干什么?”,然后便是让我们画一个发送'h'作为输入密码和另外一个值的能量消耗图
根据提示,补全代码,运行即可看到,输入'h'和'r'作为密码时,微控制器的能量消耗波形
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#Example - capture 'h' - end with newline '\n' as serial protocol expects that
trace_h = cap_pass_trace("h\n")
trace_r = cap_pass_trace("r\n")
print(trace_h)
# ###################
# Add your code here
# ###################
%matplotlib notebook
import matplotlib.pyplot as plt
plt.plot(trace_h)
plt.plot(trace_r)
plt.show()往下有以下提示信息,意思就是说:应该看到与下图1类似的结果,并且如果是使用了
%matplotlib notebook
魔法值,那么可以放大缩小查看图片这里要放大缩小图片,需要点击左下角保存图标左边的一个控件,点击后,右下角出现'zoom rect'字样即可开始放大图片,如果还需要移动,则点击移动的图标即可。
接着往下运行,可以看到以下提示信息,大意是:接下来画所有可能的密码对应的能量消耗图,这个密码的实现只在'abcdefghijklmnopqrstuvwxyz0123456789'之中。提示信息还提到上面的波形图太长了,不好看,可以将scope.adc.samples设为500
按照提示信息修改完代码后,运行效果如下
1
2
3
4
5
6
7
8
9
10
11
12
13%matplotlib notebook
import matplotlib.pyplot as plt
scope.adc.samples = 500
plt.figure()
LIST_OF_VALID_CHARACTERS='abcdefghijklmnopqrstuvwxyz0123456789'
for CHARACTER in LIST_OF_VALID_CHARACTERS:
trace = cap_pass_trace(CHARACTER + "\n")
plt.plot(trace, label=CHARACTER)
plt.legend()
plt.show()往下可以看到提示信息说,图片放大后应该可以看到有一条曲线不与其他曲线重叠在一起
事实也是如此,如下图所示,有一条曲线明显与其他曲线偏离开,这条曲线其实就意味着输入了一位正确密码,根据其颜色与旁边的图例可知改峰图可能为'h'或者'r'(这里图中两者颜色一样不好区分),但结合已知第一位是'h',所以可知实际代表的就是'h'
![截屏2022-08-14 上午10.21.00](侧信道攻击学习笔记2-密码验证的能量分析/截屏2022-08-14 上午10.21.00.png)
接着可以看到如下提示信息,让我们进行猜测第二位密码
按提示信息修改密码后,再次画图,如下图,可以看到有一条粉丝曲线明显与其他曲线不重叠,但由于图例中'g'、'q'、'0'都是粉色,所以不能直接判断是哪个,这是matplotlib的缺陷
1
2
3# 将循环的trace = cap_pass_trace(CHARACTER + "\n")改为以下代码即可猜测第二位
trace = cap_pass_trace('h'+CHARACTER + "\n")改为用plotly画图,plotly同样为python画图的库,而且对比matplotlib,plotly有个优点就是,当你鼠标悬浮在某个位置时,它会显示该点对应的曲线信息,这样一来就容易区分是哪个字符了。如下图,图中明显与其他曲线分离的是字符'0'所对应的曲线,因此,可以推断第二位密码为'0'
1
2
3
4
5
6
7
8
9import plotly.graph_objects as go
import numpy as np
fig = go.Figure()
LIST_OF_VALID_CHARACTERS='abcdefghijklmnopqrstuvwxyz0123456789'
for CHARACTER in LIST_OF_VALID_CHARACTERS:
trace = cap_pass_trace('h'+CHARACTER + "\n")
fig.add_trace(go.Scatter(x=np.array(range(trace.shape[0])), y=trace, mode='lines', name=CHARACTER,text=CHARACTER))
fig.show()
自动化判断
再往下就可以看到如下信息,大意是:假定现在不知道任何密码,要自动化判断正确密码的一个简单方法是,用'0x00'即空密码和其他输入做比较
按提示,运行后,如下图,图中的两条曲线基本上重叠在一起了,因为'c'同样不是正确密码
接下来的提示信息,就是让我们画出每个可能密码减去以'0x00'作为基线后的曲线图
按提示修改代码后,画图结果如下
1
2
3
4
5
6
7
8
9
10
11
12
13%matplotlib notebook
import matplotlib.pyplot as plt
plt.figure()
LIST_OF_VALID_CHARACTERS='abcdefghijklmnopqrstuvwxyz0123456789'
ref_trace = cap_pass_trace( "\x00\n")
for CHARACTER in LIST_OF_VALID_CHARACTERS:
trace = cap_pass_trace(CHARACTER + "\n")
plt.plot(trace - ref_trace, label=CHARACTER)
plt.legend()
plt.show()可以看到上图跟下面提示信息一致,有一条图曲线比较突出
接下来提示信息就是说:让我们利用numpy的sum和abs来计算差异
按提示修改代码,计算每种可能密码的能量消耗数据减去'0x00'为基准的数据,最后求和得出差异值。如下图所示,字符'h'拥有最大的差异值,也就是说正确密码拥有最大的差异值
1
2
3
4
5
6
7
8
9import numpy as np
ref_trace = cap_pass_trace( "\x00\n")
for CHARACTER in LIST_OF_VALID_CHARACTERS:
trace = cap_pass_trace(CHARACTER + "\n")
diff = np.sum(np.abs(trace - ref_trace))
print("{:1} diff = {:2}".format(CHARACTER, diff))接下来的提示信息,就是让我们修改代码,自动化判断出每一位正确密码
如下图,按提示信息修改代码运行后,最终得出正确密码'h0px3',如果结果是'hhhhh'或者其他则需要检查代码是否有问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import numpy as np
LIST_OF_VALID_CHARACTERS='abcdefghijklmnopqrstuvwxyz0123456789'
guessed_pwd = ""
for i in range(5):
ref_trace = cap_pass_trace(guessed_pwd + "\x00\n")
m=0
c='\x00'
for CHARACTER in LIST_OF_VALID_CHARACTERS:
trace = cap_pass_trace(guessed_pwd + CHARACTER + '\x00\n')
diff = np.sum(np.abs(trace - ref_trace))
if diff > 25:
guessed_pwd += CHARACTER
print(guessed_pwd)
break接下来看看目标板固件的源码实现,在hardware/victims/firmware/basic-passwdcheck目录下的basic-passwdcheck.c文件中,我们可以看到,main函数中定义的代码的确就是"h0px3"。从下面源码,可以推测,正确密码与错误密码会有较大的差异主要在于,正确的密码需要执行更多的循环语句
那么问题来了:把错误密码的
break
语句注释掉,还能用这个方法判断正确密码吗?按上图中修改basic-passwdcheck.c代码,重新编译并上传至目标板
再次运行画图和计算差异的代码,可以看到已经不能判断出,哪个是正确代码了
所以如果要简单防止这种攻击,一个简单方法就是即使已经判断出密码错误,也让它跑完整个循环而不是跳出循环,虽然这会造成一定性能上的损失,但是这是个简单有效的方法
总结
- 学习了如何利用能量分析来判断出简单密码验证系统中的正确密码
- 学习了如何简单地防范这种能量分析攻击