【问题标题】:Recover email address from special application of MD5 hash function从 MD5 哈希函数的特殊应用中恢复电子邮件地址
【发布时间】:2014-07-23 04:02:33
【问题描述】:

首先,我们将电子邮件地址分割成 2 个字符的字符串。
然后,对于每个段 s,我们计算以下哈希 J:

md5(md5(s) + s + md5(s))  [where + is the string concatenation operator].

最后,我们将所有哈希字符串 J 连接起来形成下面的长哈希。

例如:对于helloworld@company.com 的输入,我们会计算:

md5(md5('he') + 'he' + md5('he')) +
md5(md5('ll') + 'll' + md5('ll')) +
md5(md5('ow') + 'ow' + md5('ow')) +
...

长哈希:

f894e71e1551d1833a977df952d0cc9de44a1f9669fbf97d51309a2c6574d5eaa746cdeb9ee1a5df
c771d280d33e5672bf024973657c99bf80cb242d493d5bacc771b3b0b422d5c13595cf3e73cfb1df
91caedee7a6c5f3ce2c283564a39c52d3306d60cbc0e3e33d7ed01e780acb1ccd9174cfea4704eb2
33b0f06e52f6d5aba5a5a89e6122dd55f8efcf024961c1003d116007775d60a0d5781d2e35d747b5
dece2e0e3d79d272e40c8c66555f5525

如何从哈希中恢复电子邮件地址?据我了解,“哈希”是一种单向函数。我只能将它与另一个哈希进行比较,看看它们是否匹配或生成原始文本的哈希。

【问题讨论】:

    标签: encryption hash md5


    【解决方案1】:

    虽然一般而言从哈希中提取原始消息是不切实际的,但这显然看起来像是一个精心设计的条件以打破“加密”的练习.


    假设电子邮件地址被分成两个字符的段。如果您将自己限制为仅小写字母(26 个字母 + 2 个符号,@.,则只有 28 * 28 = 784 可能的两个字母组合。即使电子邮件有小写和大写字母和数字,也只有64 * 64 = 4096 组合——完全在计算范围内。

    要做的事情是预先计算一个rainbow table,或者你的搜索空间中所有可能的哈希值的表。你可以用一个矩阵来做到这一点:

     +----------------------------------+----------------------------------+----------------------------------------+-----------------------------+
     |                a                 |                b                 |                c                       |             ...             |
     +----------------------------------+----------------------------------+----------------------------------------+-----------------------------+
    a| md5(md5('aa') + 'aa' + m5('aa')) | md5(md5('ba') + 'ba' + m5('ba')) | md5(md5('ca') + 'ca' + m5('ca'))       |             ...             |
     +----------------------------------+----------------------------------+----------------------------------------+-----------------------------+
    b| md5(md5('ab') + 'ab' + m5('ab')) | md5(md5('bb') + 'bb' + m5('bb')) | md5(md5('cb') + 'cb' + m5('cb'))       |             ...             |
     +----------------------------------+----------------------------------+----------------------------------------+-----------------------------+
    c| md5(md5('ac') + 'ac' + m5('ac')) | md5(md5('bc') + 'bc' + m5('bc')) | md5(md5('cc') + 'cc' + m5('cc'))       |             ...             |
     +----------------------------------+----------------------------------+----------------------------------------+-----------------------------+
     |               ...                |               ...                |               ...                      |             ...             |
     +----------------------------------+----------------------------------+----------------------------------------+-----------------------------+
    

    但是您每次都必须遍历矩阵才能找到匹配项——慢!

    另一种方法是使用字典,键是哈希,值是“解码”字母:

    { 
       md5(md5('aa') + 'aa' + md5('aa')): 'aa',
       md5(md5('ab') + 'ab' + md5('ab')): 'ab',
       md5(md5('ac') + 'ac' + md5('ac')): 'ac',
      ...
    }
    

    无论哪种方式,您现在都将拥有所有可能的两个字母组合的哈希值。现在您处理输入字符串。由于 MD5 生成 32 个字符的长哈希,因此将输入分解为 32 个字符的字符串,并对您的表执行查找:

    'f894e71e1551d1833a977df952d0cc9d' => 'he'
    'e44a1f9669fbf97d51309a2c6574d5ea' => 'll'
    ...
    

    【讨论】:

    • 其实还不错。这是可能的,除非您遇到冲突,在这种情况下,您需要查看数据并进行一些猜测。从预先计算的集合中可以明显看出碰撞。不过,这不是一项简单的任务,因为您的假设是电子邮件只是字母 - 电子邮件可以有比这更多的符号 - 数字、下划线、加号,可能还有更多我不知道的符号。即便如此,也没有什么可以阻止电子邮件使用大写字母 - 可能是用户输入了 NameLastname@mail.com,而系统只是照原样接受并对其进行哈希处理。
    • 基本上,如果您没有关于数据集的更多信息,您将基本上是暴力破解。否则,您仍然会这样做,但搜索空间更有限。然而,直到现在我才注意到的一件事是,长哈希实际上会泄露电子邮件的长度。此外,由于您可以预期许多电子邮件以 .com 或 .co.uk 或 .de 等常见顶级域结尾,因此您可以在一个很多情况,因为它们是相同的。事实上,电子邮件域是另一个罪魁祸首(字符用完)
    • @Vld 是的,可以找到冲突,打印出所有可能性,但仍然只留下一小部分可能的电子邮件。至于蛮力:在这种情况下,所需的力根本没有那么大。即使您可以在一封电子邮件中包含 100 个可能的字符,按照设置问题的方式,您仍然只需要查找表中的 10,000 个条目——最坏的情况是几秒钟就可以工作。
    • 因此,例如@gmail.com 将在@符号之前的偶数个符号的任何电子邮件中加密相同。如果数字是奇数,那么gmail.com 将是一个可识别的字符串。与@yahoo.com@hotmail.com 等相同 - 获取常见邮件提供商的列表将大大减少您需要破解的电子邮件数量。假设,您有一大堆需要破解的电子邮件。如果它是一个单一的,那么你至少可能会限制选择。
    • @Vld 不限于电子邮件? 256 个 ascii 字符在表中仍然只有 65536 行。与用于破坏哈希的许多 GB 大的彩虹表相比,这简直是小菜一碟。这是一个更难的问题。这个设置是可行的。
    【解决方案2】:

    你可以这样做:

    第 1 步:将哈希字符串分成 32 位块

    第2步:从字符串列表中找出2个字符串的所有可能组合,可以是字母、数字和任何特殊字符的组合。

    步骤3:为该段生成MD5哈希码,将其与纯文本段和相同的哈希码连接并再次生成MD5哈希码

    第 4 步:将生成的哈希码与现有的哈希码进行比较。如果匹配,则将其保存在字符串缓冲区中。重复这个过程,直到所有的块都被解码。你会得到你的答案。

    【讨论】:

    • 谢谢@helper。这正是我想要的。如果确实有效,将恢复。
    【解决方案3】:

    这是你的问题在 python 中的实现。

    我的代码:

    import hashlib, string
    
    # lambda function for MD5
    md5hashFunction = lambda data: hashlib.md5(data.encode()).hexdigest()
    
    # lambda function for  md5(md5(data) + data + md5)
    finalHash = lambda data: md5hashFunction(
        md5hashFunction(data) + data + md5hashFunction(data)
    )
    
    
    # All MD5 hashes are 32 char length size therefore we need dive 32 fixed parts
    hashes = [
        "f894e71e1551d1833a977df952d0cc9d",
        "e44a1f9669fbf97d51309a2c6574d5ea",
        "a746cdeb9ee1a5dfc771d280d33e5672",
        "bf024973657c99bf80cb242d493d5bac",
        "c771b3b0b422d5c13595cf3e73cfb1df",
        "91caedee7a6c5f3ce2c283564a39c52d",
        "3306d60cbc0e3e33d7ed01e780acb1cc",
        "d9174cfea4704eb233b0f06e52f6d5ab",
        "a5a5a89e6122dd55f8efcf024961c100",
        "3d116007775d60a0d5781d2e35d747b5",
        "dece2e0e3d79d272e40c8c66555f5525",
    ]
    
    
    # Enumurate all alphabet and extra characters for decryption => "_+.@"
    alphabet = list(
        string.ascii_lowercase + string.ascii_uppercase + string.digits + "_+.@"
    )
    
    # Create python dictionary for map hashes to string
    rainbowTable = {finalHash(x + y): x + y for x in alphabet for y in alphabet}
    """
    rainbowTable
    
    '31453dd786a8c6f6c7c8860d5fcea4be': 'aa',
     '857dce5bcf6b6b32bec281207b2dba80': 'ab',
     'e90d94b4b65ac19188fdae82acf7fbbc': 'ac',
     '67299b8cedc5eafea7dda1daf9356b54': 'ad',
     '40fca4e80bfc6e1faa2c4e2b7e0929f0': 'ae',
     'de48fc1bd98f5508c513f9947a514ce8': 'af',
     '4852089b1b43b45204907df0066c0edf': 'ag',
     'e1b82a5fe4fdcf73d034a0d5063ffe3f': 'ah',
             ...... Continues....
    
    """
    
    # Search for matched hash and join to single string
    print("".join([rainbowTable[hash] for hash in hashes]))
    
    
    """
    f894e71e1551d1833a977df952d0cc9de44a1f9669fbf97d51309a2c6574d5eaa746cdeb9ee1a5df
    c771d280d33e5672bf024973657c99bf80cb242d493d5bacc771b3b0b422d5c13595cf3e73cfb1df
    91caedee7a6c5f3ce2c283564a39c52d3306d60cbc0e3e33d7ed01e780acb1ccd9174cfea4704eb2
    33b0f06e52f6d5aba5a5a89e6122dd55f8efcf024961c1003d116007775d60a0d5781d2e35d747b5
    dece2e0e3d79d272e40c8c66555f5525
    
    """
    
    
    """
    Output ==> secret_jobs@anvato.com
    """
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-01-22
      • 2012-06-20
      • 2019-11-06
      • 2015-11-17
      • 2010-10-03
      相关资源
      最近更新 更多