什么是彩虹表?
一言以蔽之,彩虹表是一种破解用户密码的辅助工具。彩虹表以时空折中理论为基础,但并不是简单地“以空间换时间”,而是一种“双向交易”,在二者之间达到平衡。1980年,公钥密码学的提出者之一Hellman针对DES算法(一种对称加密算法)提出了一种时空折中算法,即彩虹表的前身:预先计算的散列链集。2003年瑞典的Philippe Oechslin在其论文Making a Faster Cryptanalytic Time-Memory Trade-Off(参考博客2)中对Hellman的算法进行了改进,并命名为彩虹表。当时是针对Windows Xp开机认证的LM散列算法。当然,目前除了破解开机密码,彩虹表目前还能用于SHA、MD4、MD5等散列算法的破译,速度快、破解率高,正如Philippe在论文中提到的:“1.4G的彩虹表可以在13.6s内破解99.9%的数字字母混合型的Windows密码“。实际上,Philippe所做的改进本质上是减少了散列链集中可能存在的重复链,从而使空间的有效利用率更高,关于这一点,后面会详述。
为了便于叙述,本文以MD5为示例(实际应用中,不推荐使用MD5,而推荐SHA256, SHA512, RipeMD, WHIRLPOOL等),即以MD5作为明文的散列算法H,加密后的密文为q,明文为p。彩虹表的作用就是在已知散列算法H和散列后的密文q的情况下,快速地得到明文p。除了MD5,参考博客3中还给出了针对LM、NTLM和SHA1等算法对应的彩虹表。整个破解过程为:根据密文q所使用的散列算法H,生成或者下载针对该算法的彩虹表,然后再基于彩虹表对密文进行破解,最终得到明文p。
不可逆的散列算法(HASH)
哈希(Hash)算法就是单向散列算法,它把某个较大的集合P映射到另一个较小的集合Q中,假如这个算法叫H,那么就有Q = H(P)。对于P中任何一个值p都有唯一确定的q与之对应,但是一个q可以对应多个p。作为一个有用的Hash算法,H还应该满足:H(p)速度比较快; 给出一个q,很难算出一个p满足q = H(p);给出一个p1,很难算出一个不等于p1的p2使得 H(p1)=H(p2)。
MD5是一种常见的散列算法。由参考博客5可知,本质上,MD5是将明文对应的二进制值与四个特定的32位的二进制值进行多轮的与、或、非、异或等运算,最终将明文对应的二进制转换成新的二进制。由于运算过程中存在进位丢失,导致人们无法根据MD5的计算过程和最终结果逆向计算出对应的明文。
这里还想补充的一点是:MD5常常和Base64编码一起使用。原因是:MD5的输入是明文对应的二进制,输出也是二进制,但输出的二进制无法保证还能解析成可打印字符(以ASCII码为例,其第0~32号及第127号是控制字符或通讯专用字符,不便于打印/显示/存储),而Base64恰好是一种网络上最常见的基于64个可打印字符来表示二进制数据的方法。以下分析都默认执行了Base64的转换。
彩虹表的前身:预先计算的散列链
由前面的描述已知,由于MD5具有不可逆性,因而即使我们知道了加密算法H和加密后的密文q,我们也无法找到一个反函数R,使得p=R(q)=R(H(p))。因而本文介绍的所有破解算法(包括彩虹表)本质上都属于穷举法。
穷举法
为了根据H和q获取p,我们首先想到了最简单的穷举法。假设p字符集为alpha-numeric(参考博客4,即p由数字和字母组成),穷举法就是穷举所有的字母和数字的组合,依次执行H运算,并将运算结果和q进行比较:如果不相同则继续寻找下一个;如果相同,则由于MD5不保证H(p1)=H(p2)一定不存在(概率极低),因而此时极大概率获取到的就是明文。显然,随着明文字符集的扩充(比如包括!@#$%等特殊符号)和密码长度的增加,穷举法的破解耗时之长,可想而知。参考博客6中就提到:以14位字母和数字的组合密码为例,共有1.24×10^25种可能,即使电脑每秒钟能进行10亿次运算,也需要4亿年才能破解。
字典表
为了解决穷举法耗时长的问题,于是有了字典表,该方法就是事先将字符集中所有可能的组合执行H运算,并将明文及其对应的运算结果以(明文,密文)对儿的方式存储起来。执行破解时通过查询字典表的方式,匹配密文的明文即为需要破解的密码。还是以14位字母和数字的组合密码为例,生成的密码32位哈希串的对照表将占用5.7×10^14 TB的存储空间。如果增加密码长度或添加符号,需要的时间或磁盘空间将更加难以想象。
预先计算的散列链集