By Jake Edge
June 22, 2011
翻译:史玉良
最近在crypt_blowfish密码散列库中发现了一个长期以来一直存在的bug。由于crypt_blowfish加密算法已经存在多年(据称该bug可以追溯到1998或1997年!),并已广泛应用于其他安装包(例如PHP)和一些Linux发行版中;从该事件可以看到,一个广泛应用的底层库若出现bug时可能会引发相当大的麻烦。该bug对于安全性的影响不大,因为它只影响到一些不常用于做口令的字符。但对于那些存储了由该库函数而产生的散列密码的部分人来说,这个bug就会有些头疼。
密码散列技术是一个用于鉴权机制(例如登陆一个系统或网络应用)的标准技术。明文密码并不需要存储,而由明文密码派生出来的密文需要存储。SHA-1和Blowfish cipher是比较有代表性的用于该目的的单向加密散列技术。当用户输入密码时,调用相同的Hash函数,并将生成的密文与已经存储的密文做比对。这个想法是,即使攻击者侵入了密码数据库,他们也需要破解那里存储的密码散列值才行。正如其名称所示,crypt_blowfish是在Blowfish算法的基础上实现的密码散列技术。
这个bug本身很简单,其修复方法在C程序员看来是显而易见的。在面向对象的程序设计系统中,需要把 BF_std.c: BF_std_set_key()中的tmp |= *ptr;
改为:
tmp |= (unsigned char)*ptr;
基本上来说这是一个符号扩展的bug。对于那些不是C程序员的黑客,可能需要一点解释。当存储在*ptr中的值高位被置为1时,它将被视为一个负数。因此,在该值与tmp(一个无符号整型)做或运算之前,它会被提升到4字节并做符号扩展。例如一个byte值0x80会变成0xffffff80。正如人们所预料的那样,之后的或运算将会产生一个错误结果。
这个问题实际上是由John the Ripper(JtR)口令破译软件程序发现的。作为创建测试套件的一部分, "magnum"(万能)尝试去破解由一个库(并非crypt_blowfish)的散列函数产生的包含有单个非ASCII字符(£或0xa3)的口令。由于JtR和crypt_blowfish 共用相同的Blowfish加密算法实现,同时使用时测试会通过,但是算法其它的实现将产生正确的散列,从而会不匹配JtR所产生的散列。
JtR和crypt_blowfish的开发者Alexander Peslyak(同时也是Solar的设计者)分析了这个bug产生的影响并发现,一些口令组合通过散列运算会产生差异很小的相同值(例如"ab£" 产生的值为"£"),这将会使得口令破译变得简单。进一步的分析显示,在最高位被置1的字符前出现的一些字符在计算散列值时会被有效的忽略。这意味着一个比用户给出的密码更简单的密码也会被视为有效——这是对用户口令作用的一个重大削弱。
应该指出的是,Solar的设计者关于这个问题的细节说明及其产生的影响即将公布。他的CVE(通用漏洞披露)要求相当详尽,并且他为这个错误承担所有的责任。其他发现projects中存在安全漏洞的程序员也应该按照他的先例来处理类似情况。
JtR并没有什么实际问题,由于它可以更新算法,所以它可以正确破解高比特位设置为1的密码。此外,它可以继续使用老的算法来破解由于hash函数错误生成的密码。但是使用crypt_blowfish的应用就不可同日而语了。高位被置位可能相当少见——至少对于那些只服务ASCII用户的网站是这样,但是目前还没有简单的方法来判别存储的散列值是否有效。
对可能存在错误的密码散列值(包括现行的Openwall Linux (Owl), SUSE和 ALT Linux上的用crypt_blowfish算法产生散列值存储的密码数据库)的管理员来说,最安全的解决方案就是使现有的所有密码失效而后要求用户重新设置密码。这简直是逻辑噩梦。不过,这取决于如何简单、安全并且不需要通过现有密码鉴权就能使用户可以设置新密码。要求用户登陆以后改变现有密码是一种替代方案,不过这可能会给攻击者可乘之机。它同时为所有未登陆的用户保留原有密码不变。
攻击者利用这一漏洞登陆一个网站的风险相当小,但却是存在。如果一个网站的登陆过程容易受到暴力攻击,那么这个bug会使这个攻击变得容易,至少对于特定的密码类型是这样的。另一方面,如果密码数据库已经暴露出来,并且一些密码没有被破解(至少对于没有使用JtR程序的攻击者),这个信息可以给他们提供破译那些密码的方法。最终的分析结论是,这是一个可以被利用的,但并不会让管理员们陷入恐慌的漏洞。
令人惊讶的是这个bug在广泛被使用的库中存在了超过了13年!到目前为止,没有人用这种方式测试它,至少没有公开。这应该给那些使用固定版本软件(包括免费的和专有的)一些警示。自由软件(crypt_blowfish已经被置于公共领域,这可能在法律上有点模糊,但是这与自由软件的理念是一致的)有更多,更容易的机会来检查和测试代码,不过,只有实实在在的测试才是有效的。
毫无疑问,bugs依旧潜伏于我们每天所依赖的各种面向安全的库中,因此,定期地、系统地测试这些类库(还有那些没有用于安全用途的代码)可以帮助我们查出bugs。虽然超过10年才发现了这一bug,但值得指出的是,在此期间它可能已经被其他人发现。攻击者显然也在做自己的测试工作,不过他们的测试结果通常不会公布出来。可能和这里讨论的情况不一样,不过应该始终认识到,公开披露一个漏洞和它被发现之间是没有必然联系的(可能在被公开之前已经有人发现了)。
最新评论