Loading...
墨滴

yuanzhoulvpi

2021/07/18  阅读:52  主题:橙心

python使用时间破解密码

背景

之前看到过很多文章,介绍如何使用python来破解一个带有密码的压缩包,这类破解方法都是太暴力了,是不断的去生成一个字符串然后去检验是否正确,不断的扩大这个字符串的长度,不断的变化这个字符串的内容去匹配。感觉就是很暴力、不太优雅。

这类破解的方式暴力、不优雅不是体现在他们使用排列组合去尝试密码多少,而是说他们在破解的时候,只是考虑每一次撞击密码的结果(结果正确与不正确)这个单一维度,但是我觉得还可以再考虑一个维度:这个黑箱返回结果的时间。

代码实现

我们可以这么想,如果有一个数据库,这个数据库在判断用户登陆的时候,会这么判断,先看这个人输入的密码长度和真实的密码长度是不是一样的?如果长度不一样,那么立即返回密码错误这个结果;如果长度是一样的,那么就判断输入的密码和真实的密码的每一个位置的字符是不是都是一样的?如果都一样,那么就返回密码正确,如果有一个不一样的,立即反馈密码错误这个结果。

密码检测_检测密码是否正确

为了实现这个功能的密码检测,我写了一个非常简单的python代码,代码如下:

import string
import random
import timeit
import numpy as np
import itertools

allchar = string.ascii_lowercase + ' ,;:'
userdatabase = {'yuanz''gzh: pypi; desc: zhnb;'}


def check_password(user, guess_pd):
    actual_pd = userdatabase[user]

    if len(actual_pd) != len(guess_pd):
        return False

    for i in range(len(guess_pd)):
        if guess_pd[i] != actual_pd[i]:
            return False

    return True

在上面的check_password函数中,我们可以很明显的得到下面结论:

  1. A = 长度和真密码长度相同的假密码在返回结果的时间;B = 长度和真密码长度不同的假密码在返回结果的时间,A > B。因为假密码的长度和真密码的长度相同的时候,假密码还要做而外的检验,从而他们在检验的时间会相对来说变长。

  2. 一个假的密码和真密码相似度越高,这个假密码检验的时间就越长。举个例子来说:一个真的密码是:abcxyz, 一个假的密码是abyyyy,一个假的密码是abcxyy,因为abcxyy在check_password中的需要匹配到底6个字符串才知道是错误的,但是abyyyy在匹配第三个字符的时候,就知道这个字符串是错误的。因此,密码检验时间越长,说明密码越接近真实密码。

密码检测_检测密码的长度

按照上面的规则检测密码的长度,代码如下:


def random_str(size):
    return ''.join(random.choices(allchar, k=size))


def guess_length(user, max_length=32, verbose=False) -> int:
    trials = 10000

    times = np.zeros(shape=max_length)

    for i in range(max_length):
        i_time = timeit.repeat(stmt="""check_password(user, x)""",
                               setup=f"user={user!r}; x=random_str({i!r})",
                               globals=globals(),
                               number=trials,
                               repeat=10)
        times[i] = min(i_time)

    if verbose:
        most_likely_n = np.argsort(times)[::-1][:5]
        print(most_likely_n, times[most_likely_n] / times[most_likely_n[0]])

    most_likely = int(np.argmax(times))

    return most_likely

密码检测_检测密码的具体字符

在知道密码具体的长度以后,就可以进一步的检测密码的每一位字符,代码如下:


def guess_str(user, length, verbose=False):
    guess = random_str(size=length)
    trials = 1000
    counter = itertools.count()

    while True:
        i = next(counter) % length
        for c in allchar:
            alt = guess[:i] + c + guess[(i + 1):]

            alt_time = timeit.repeat(stmt="""check_password(user, x)""",
                                     setup=f"user={user!r}; x={alt!r}",
                                     globals=globals(),
                                     number=trials,
                                     repeat=10)

            guess_time = timeit.repeat(stmt="""check_password(user, x)""",
                                       setup=f"user={user!r}; x={guess!r}",
                                       globals=globals(),
                                       number=trials,
                                       repeat=10)

            if check_password(user=user, guess_pd=alt):
                print(alt)
                return alt

            if min(alt_time) > min(guess_time):
                guess = alt
                if verbose:
                    print(guess)

运行

最后,将上面的代码都放在一起,写个main函数,就能破解出来,剩余python代码如下:

def main():
    pd_length = guess_length(user='yuanz', verbose=True)
    print(pd_length)

    password = guess_str(user="yuanz", length=pd_length, verbose=True)
    print(f"password: {password!r}")


if __name__ == '__main__':
    main()

总结

  1. 感觉昨天自己说大话了,其实这样的破解方法不一样比那个暴力破解就更好,我这个破解方法还是有很大的局限性。

  2. 你怎么就知道一个密码的校验方式就是先检验密码的长度是否匹配,然后再从左向右以此检验每一个字符串呢。

  3. 而且我这个代码去不断的询问真实的密码,这个在现实中是遇不到的,大部分人的密码校验如果错误次数超过若干次就会要输入验证码或者别的字符串之类的,防止是一个机器人🤖️。

  4. 我这个代码严重依靠返回结果的时间,这就导致如果我的电脑在跑别的大型程序,就很难统计这个结果返回的时间,如果我访问的这个密码还是在web端,那么这个返回时间更加不准确了。

  5. 本次就是给大家图个乐,如果想看源码,可以查看下方的GitHub链接。

  6. 代码链接:https://github.com/yuanzhoulvpi2017/tiny_python/tree/main/time_password

yuanzhoulvpi

2021/07/18  阅读:52  主题:橙心

作者介绍

yuanzhoulvpi