现在OpenSSL的心血(Heartbleed)漏洞已经是人尽皆知了: 在最流行的TLS实现之一的OpenSSL中由于缺少的一个边界检查导致了数以百万计(或更多)的Web服务器泄露了内存中的各种敏感信息.这会将登录证书, 认证cookie和网站流量泄露给攻击者. 但它能否用于获取站点的私钥? 取得站点的私钥就可以破解以前记录的没有达成完美的向前保密性的流量, 然后就可以在以后的TLS会话中实施中间人攻击.
因为这是心血漏洞的更为严重的后果, 我决定尝试下. 结果证明这个猜想是正确的. 经过几天的攻关, 我能够从一台测试Nginx服务器上提取到私钥. 稍后我会用我的技术来解决CloudFlare Challenge. 与另外几名安全研究人员一起, 我们独立地证明了RSA私钥的确是处于危险之中. 下面我们来详细介绍私钥是怎样提取的和为什么这种攻击是可能的.
注: CloudFlare Challenge 是cloudflare.com发起的一项挑战: 从他们搭建的测试nginx服务器(安装了有heartbleed漏洞的OpenSSL)上窃取私钥.
相关阅读:
OpenSSL TLS心跳读远程信息泄露漏洞 (CVE-2014-0160)
OpenSSL严重bug允许攻击者读取64k内存,Debian半小时修复
通过OpenSSL提供FTP+SSL/TLS认证功能,并实现安全数据传输
OpenSSL “Heartbleed”心脏流血漏洞升级方法
怎样提取私钥不熟悉RSA的读者可以在这了解下. 为了简单点, 由两个随机生成的大的素数p和q相乘得到一个大的数(2048 bits) N. N会被公开,但p和q却不会. 找到p或q就可以反算出私钥. 一个通用的攻击方式是对N做因式分解, 但这是很困难的. 然而, 通过像心血(Heartbleed)这样的漏洞, 攻击会变得简单很多: 因为Web服务器需要将私钥保存在内存中来签名TLS的握手协议, p和q必须在内存中而且我们可以试着用heartbleed的网络包来取得它们. 这个问题很简单地就变成了如何从返回的数据中找到它们. 这也很简单,因为我们知道p和q的长度是1024 bit(128字节), 并且OpenSSL在内存中以小字节序表示数据. 一个野蛮方法是将heartbleed数据包中每个连续的128字节做为一个小字节序数值然后测试这个数值是否能整除N, 这个方法足以发现潜在的漏洞. 这也是人们解决CloudFlare challenge的方法.
Coppersmith 改进但是等等,和我们冷起动内存映像攻击的方案不同。已经有很多通过部分消息恢复RSA的研究。最著名的是一份来自Coppersmith的论文,介绍了通过相关消息或者填充不足消息,以及在格基简化算法帮助下因式分解部分消息来攻击。通过 Coppersmith攻击,只需知道P的上部或下部,N就可以有效地因式分解。基于此,和暴力破解所需的128字节相比,我们只需要上部或下部的64字节就能计算出秘钥。在实践中,Coppersmith的限制是计算开销(但任然比因式分解好很多了),假设已知77字节(60%),我们可以非常迅速地梳理出“心血”包的潜在秘钥。
回想起来,我已收集的超过10000个包(每个64KB)有242个私钥的残余适合Coppersmith攻击。感谢Sage(虽然后来我发现Sage已经实现了Coppersmith攻击)的全面计算机代数积木使Coppersmith攻击的实现变得更容易。
我们能做的更好吗?加入你曾经用openssl ras -text -in server.key命令查看过RSA私钥,你会发现有相当多的数字超过两个素数因子p和q。实际上,他们是为了优化预先计算的值。如果他们中的一些是遗漏了,他们也能被p推演出来。OpenSSL是怎样将p和q的Montgomery representations用于快速乘积的?他们也承认Coppersmith的变体,以便局部的位也是有用的。考虑到这一点,我们开始在我的测试服务器上搜索已收集的包。但是在数据集中甚至没有发现单独出现的部分(大于16个字节)。这怎么可能呢?
注意:我的所有实验和CloudFlare challenge的目标是单线程的Nginx。在一个多线程的web服务器上也是可能的,能观察到更多的泄露。
为什么只会泄露p