计时攻击

7erry

概念

计时攻击,也叫时序攻击,TimingAttack,是侧信道攻击的一种,它通过设备运算的用时来推断出所使用的运算操作,或者通过对比运算的时间推定数据位于哪个存储设备,或者利用通信的时间差进行数据窃取

实例

假设后端存在这样一个逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    #! /bin/go
func equal(password string) bool {
TRUE_PASSWORD := "PASSWORD"
length := len(password)
if length == len(TRUE_PASSWORD) {
for i := 0; i < length; i++ {
if password[i] != TRUE_PASSWORD[i] {
return false
}
}
return true
}
return false
}

即获取用户输入的密码,然后逐位判断输入的密码与真正的密码是否相符,如果不符合就返回失败。这样的逻辑就存在被计时攻击的漏洞。因为你很容易发现,输入的密码第一位正确与第一位错误时或输入的密码长度与正确密码长度相等或不等时程序的执行时间是不同的,这意味着攻击者可以根据程序执行的时间判断自己输入的密码前几位是否正确。这与SQL注入的时间盲注与布尔盲注的攻击逻辑相同。

虽然对于上述代码而言,输入的密码长度与正确密码长度相等或不等时程序的执行时间不同,但这一点并没有太大的利用空间,因为后端基本对用户输入进行哈希运算得到一样长的数据

因此,这段程序更为安全的写法应该是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    #!env /bin/go
func safe_equal(password string) bool {
TRUE_PASSWORD := "PASSWORD"
flag := 1
for i := 0; i < len(password); i++ {
if password[i] != TRUE_PASSWORD[i] {
flag = 0
}
}
if flag{
return true
}else{
return false
}
}

对于比这个例子更复杂却存在相同漏洞的算法,我们完全可以使用相同的攻击方式,例如针对HMAC的攻击

针对HMAC的攻击

HMAC,就是客户端向服务端发来一个字符串和其签名字符串(HMAC),然后,服务端的程序用一个私钥来对客户端发来的字符串进行签名得到签名字符串,然后再比较这个签名字符串。而如果这个字符段比对存在上述程序所说的问题,那么攻击者可以在不知道签名算法和私钥,但是知道API的调用接口的情况下,一遍穷举签名,一遍统计调用时间的方式来非常有效的破解签名。例如对于一个签名ddacdffbf0abcddrcdf59ccc19625015b33f55ab,攻击者从0000000000000000000000000000000000000000开始穷举,并得到了穷举第一个字符(从 0 到 f 是因为这是 HMAC 算法的取值范围)的时间统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0 0.005450913
1 0.005829198
2 0.004905407
3 0.005286876
4 0.005597611
5 0.004814430
6 0.004969118
7 0.005335884
8 0.004433182
9 0.004440246
a 0.004860263
b 0.004561121
c 0.004463188
d 0.004406799
e 0.004978907
f 0.004887240

原始的数据比较脏,因此我们想要得到得信息并不显著,而我们如果采用这样一个算法,向服务器分别从 0 到 f 发出 16个请求,并记录每个请求的响应时间,并将它们排序 1-16,其实 1 对最慢的请求,16 是最快的请求,分别记录 0-f 的名次,然后重复上述的过程 500 次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"0"=>[7, 1, 3, 3, 15, 5, 4, 9, 15, 10, 13, 2, 14, 9, 4, 14, 7, 9, 15, 2, 14, 9, 14, 6, 11...],
"1"=>[13, 4, 7, 11, 0, 4, 0, 2, 14, 11, 6, 7, 2, 2, 14, 11, 8, 10, 5, 13, 11, 7, 4, 9, 3...],
"2"=>[14, 5, 15, 5, 1, 0, 3, 1, 9, 12, 4, 4, 1, 1, 8, 6, 9, 4, 9, 5, 8, 3, 12, 8, 5...],
"3"=>[15, 2, 9, 7, 2, 1, 14, 11, 7, 8, 8, 1, 4, 7, 12, 15, 13, 0, 4, 1, 7, 0, 3, 0, 0...],
"4"=>[12, 10, 14, 15, 8, 9, 10, 12, 10, 4, 1, 13, 15, 15, 3, 1, 6, 8, 2, 6, 15, 4, 0, 3, 2...],
"5"=>[5, 13, 13, 12, 7, 8, 13, 14, 3, 13, 2, 12, 7, 14, 2, 10, 12, 5, 8, 0, 4, 10, 5, 10, 12...]
"6"=>[0, 15, 11, 13, 5, 15, 8, 8, 4, 7, 12, 9, 10, 11, 11, 7, 0, 6, 0, 9, 2, 6, 15, 13, 14...]
"7"=>[1, 9, 0, 10, 6, 6, 2, 4, 12, 9, 5, 10, 5, 10, 7, 2, 4, 14, 6, 7, 13, 11, 6, 12, 4...],
"8"=>[4, 0, 2, 1, 9, 11, 12, 13, 11, 14, 0, 15, 9, 0, 0, 13, 11, 13, 1, 8, 6, 5, 11, 15, 7...],
"9"=>[11, 11, 10, 4, 13, 7, 6, 3, 2, 2, 14, 5, 3, 3, 15, 9, 14, 7, 10, 3, 0, 14, 1, 5, 15...],
"a"=>[8, 3, 6, 14, 10, 2, 7, 5, 1, 3, 3, 0, 0, 6, 10, 12, 15, 12, 12, 15, 9, 13, 13, 11, 9...],
"b"=>[9, 12, 5, 8, 3, 3, 5, 15, 0, 6, 11, 11, 12, 8, 1, 3, 1, 11, 11, 14, 5, 1, 2, 1, 6...],
"c"=>[6, 7, 8, 2, 12, 10, 9, 10, 6, 1, 10, 8, 6, 4, 6, 4, 3, 2, 7, 11, 1, 8, 7, 2, 13...],
"d"=>[2, 14, 4, 0, 14, 12, 11, 0, 8, 0, 15, 3, 8, 12, 5, 0, 10, 1, 3, 4, 12, 12, 8, 14, 8...],
"e"=>[10, 8, 12, 6, 11, 13, 1, 6, 13, 5, 7, 14, 11, 5, 9, 5, 2, 15, 14, 10, 10, 2, 10, 4, 1...],
"f"=>[3, 6, 1, 9, 4, 14, 15, 7, 5, 15, 9, 6, 13, 13, 13, 8, 5, 3, 13, 12, 3, 15, 9, 7, 10...]
}

再求每个字符排名的平均值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"f", 5.302
"0", 7.17
"6", 7.396
"3", 7.472
"5", 7.562
"a", 7.602
"2", 7.608
"8", 7.626
"9", 7.688
"b", 7.698
"1", 7.704
"e", 7.812
"4", 7.82
"d", 7.826
"7", 7.854
"c", 7.86

这样,第一位是f就显而易见了。然后,再对剩余的 39 个字符重复此算法。这是一种统计技术 ,可让我们从噪声中滤出真实的信号。因此,总共需要调用:16500400 = 320,000 个请求,而蛮力穷举需要花费 16^40 个请求,破解效率的提升不言而喻。这样,TimingAttack就实现了

  • Title: 计时攻击
  • Author: 7erry
  • Created at : 2023-07-23 00:00:00
  • Updated at : 2023-07-23 00:00:00
  • Link: http://7erry.com/2023/07/23/计时攻击/
  • License: This work is licensed under CC BY-NC 4.0.
On this page
计时攻击