MySQL OLD_PASSWORD 密码分析?

信息安全 密码学 密码 哈希 mysql 密码分析
2021-08-16 12:25:13

用于 MySQL 4.1 之前的密码(现在称为OLD_PASSWORD())的密码哈希看起来像一个非常简单的临时哈希,没有盐或迭代计数。在Django 片段中查看例如 Python 中的实现 :Old MySQL Password Hash

是否已通过密码分析?破碎的?

我在网上看到的都是蛮力攻击。正如人们所期望的那样,这些对于中短长度密码非常成功。虽然我也想知道这个 ad-hoc 算法的蛮力是否比对PASSWORD()简单使用 SHA1(两次)的新函数的攻击慢,因为我假设对 SHA1 有更广泛的硬件加速支持。寻找使用无盐哈希的知名应用程序示例中查看有关 MySQL 密码的缺陷和攻击的更多信息

3个回答

我不知道有任何关于 MySQL 的已发表密码分析OLD_PASSWORD(),但它太弱了,简直是个笑话。它可以作为密码学课程中的练习。

更新: 类似于下面描述的中间相遇的密码分析发表在2006 年国际信息安全和密码学会议 - ICISC 2006上的F. Muller 和 T. Peyrin “基于 T 函数的散列函数的密码分析”,具有更通用的描述,并进行了一些优化以查找短密码并保存在 RAM 中。

例如,这是一个“恢复”内部状态的 C 代码:

static int
oldpw_rev(uint32_t *pnr, uint32_t *pnr2, uint32_t add,
        unsigned char *cc, unsigned len)
{
        uint32_t nr, nr2;
        uint32_t c, u, e, y;

        if (len == 0) {
                return 0;
        }

        nr = *pnr;
        nr2 = *pnr2;
        c = cc[len - 1];
        add -= c;

        u = nr2 - nr;
        u = nr2 - ((u << 8) ^ nr);
        u = nr2 - ((u << 8) ^ nr);
        nr2 = nr2 - ((u << 8) ^ nr);
        nr2 &= 0x7FFFFFFF;

        y = nr;
        for (e = 0; e < 64; e ++) {
                uint32_t z, g;

                z = (e + add) * c;
                g = (e ^ z) & 0x3F;
                if (g == (y & 0x3F)) {
                        uint32_t x;

                        x = e;
                        x = y ^ (z + (x << 8));

                        x = y ^ (z + (x << 8));
                        x = y ^ (z + (x << 8));
                        nr = y ^ (z + (x << 8));
                        nr &= 0x7FFFFFFF;
                        if (oldpw_rev(&nr, &nr2, add, cc, len - 1) == 0) {
                                *pnr = nr;
                                *pnr2 = nr2;
                                return 0;
                        }
                }
        }

        return -1;
}

当在数组len中给出的密码字符(,两个 31 位字,以及密码字符之和的值)之后给出内部状态时,此函数计算插入密码之前之前的有效解人物。这是有效的。cc[]nrnr2addnrnr2

这导致了一个简单的中间相遇攻击。考虑 14 个小写 ASCII 字母的序列,这样每个字母后面跟着它的补码('a' 的补码是 'z','b' 的补码是 'y',等等......)。大约有 80 亿个这样的序列。请注意,任何这些序列的字符总和始终是固定值 1533。取其中N个序列;对于它们中的每一个,用 计算相应的哈希OLD_PASSWORD()值,并将值累积到一个大文件中:每个条目包含字符序列,以及对应的nrand nr2然后按 62 位nr/nr2对对文件进行排序。这个文件是一个大表:“使用这个序列,我们从初始值到那个 内部状态”。

然后再次获取N个序列,这次使用oldpw_rev()(如上所示)对它们中的每一个,使用实际被攻击的散列作为起点,对于nrnr22*1533 == 3066 add这将为您提供N其他对nr/ nr2,每对都有相应的序列。这些值累积在另一个文件中,您再次按 62 位nr/nr2对排序。第二个文件是一个大表:“在那个内部状态上使用这个序列,我们获得了我们当前正在攻击的哈希值”。

此时您只需找到两个匹配的对,即第一个文件和第二个文件中的相同nr/ nr2相应的 14 个字符序列是与哈希输出匹配的 28 个字符密码的两半。这可能不是最初使用的密码,但这是一个将哈希到相同值并被 MySQL 接受的密码。当N达到 20 亿左右时,您获得匹配对的可能性很高(我们在大小为2 62的空间中,因此N大约为sqrt(2 62 )就足够了)。

这种攻击的工作因数约为2 37(考虑到排序步骤),远小于理论上可以使用具有 62 位输出的哈希函数实现的2 62工作因数(对于适当的安全性来说,这已经太低了) )。因此,该OLD_PASSWORD()功能被密码破解。

(可能有比这更好的攻击。)

昨天我擦亮了我的 google-fu,这次设法找到了一篇 2006 年的论文:F. Muller 和 T. Peyrin 在国际信息安全和密码学会议 - ICISC 2006 上的“基于 T-Function-Based Hash Functions 的密码分析”做得好!

但它描述的函数与实际的 MySQL OLD_PASSWD(又名 libmysql/password.c hash_password())略有不同,因此它们之间的冲突不太有效。

论文说“如果我们选择密码“ MySQL123 ”。密码的哈希是(nk1 & K = 0x1b03947c, nk2 & K = 0x1d6d177b),其中K = 0x3fffffff。 “并说RGp0mA23的哈希是相同的。

但:

mysql> select OLD_PASSWORD('MySQL123');
+--------------------------+
| OLD_PASSWORD('MySQL123') |
+--------------------------+
| 1b03947a6f1ae59b         |
+--------------------------+

与 RGp0mA23 的 1b03947a77350d71 相比。

所以大概还有一些工作要做来解决 MySQL OLD_PASSWORD。

无论如何,请停止使用 MySQL 默认密码哈希(旧的或新的)并使用至少与1978 年 Unix 的“crypt”一样先进的技术,包括盐和迭代计数......

CVE-2003-1480针对 MySQL 的 OLD_PASSWORD 散列函数发布。从公告看来,主要问题是它的速度太快了从 MySQL 开发团队的角度来看,我可以看到他们希望如何尽可能减少数据库的负载。使用双 sha1 是键加强的一种努力。说实话,double sha1 还是很快的,只运行两次只是为了安抚CVE。