如何利用心血漏洞来获取网站的私有 crypto 密钥(2)

当"心血"首次出现时, 有人争辩说RSA私钥不会泄漏. 毕竟它们只会在Web服务器启动时被加载, 所以它们位于较低的内存地址中. 而且随着堆内存的向上增长, 随后分配的被"心血"泄漏的缓冲区内存是访问不了这些私钥的. 这与我不能发现CRT预先计算的值是一样的, 但不知为何p确实是泄漏了. 如果我们假设这个辩论是正确的, 问题就成了:为什么p被泄漏了?

另外, OpenSSL会清除掉所有用过的临时BigNum. 为了减少动态分配临时值引起的开销, OpenSSL提供了一个以栈格式操作的BigNum池---BN_CTX. 一旦使用结束, 它的上下文会被销毁并且所有分配的缓冲区也会被清除(). 这意味着当创建完"心血"数据包后在内存中不会再有任何临时数据(假设是单线程), 因为BN_CTX早就被释放了.

我不会用我查明原因时所经历过的痛苦来阻挠你, 所以下面我给出了答案:

当一个BigNum被到一个更大的缓冲区时, 它原来的缓冲区在释放前不会被置0. 导致p泄漏的控制流路径链变得更细微了. 在初始化TLS握手期间, 服务器密钥的交换是用私钥签名的. CRT签名执行了一次操作, 这导致了p<<BN_BITS2的结果被到了从BN_CTX池分配的临时变量中. 在稍后的CRT 错误注入检查中, 这个临时变量又作为被重用了(记住BN_CTX的操作与栈类似). 一个有趣的事实是被重新分配的临时变量只把它最低内存置0了, 所以对于p<<BN_BITS2什么都没有被破坏(它的最低内存本身就是0). val[0]马上接收Montgomery-reduced的值, 但因为初始缓冲区不足以储存这个新的值, 它会扩大, 所以p又被释放到了空闲堆空间中, 等待再次被使用. 因为这在每次TLS握手时都会发生, 它会被泄漏得到处都是.

因为很难找出是哪个BigNum会被扩大并引起静态泄漏, 我用工具对OpenSSL做了点实验. 结果证明了一个升级版本的p Montgomery表示也会在泄漏点被释放, 但这只发生在Montgomery上下文初始化的首次RSA幂运算中. 它会一直存在于低内存地址中, 并且我不能在抓取的数据包中找到它.

上面的泄漏bug已经通知了OpenSSL小组. 虽然有点可怕, 但严格来说这并不是安全bug, 因为OpenSSL在设计时就没想过要阻止敏感信息在堆上的泄漏.

Rubin Xu是一位剑桥大学在计算机实验室安全组攻读博士学位的博士生, 他的论文是关于移动安全, 并且他对密码学也有兴趣. 他是成功攻破CloudFlare Challenge的四人之一. 这篇博文首发于Light Blue Touchpaper blog. Rubin Xu感谢Joseph Bonneau对此文的建议和校对.

Heartbleed 的详细介绍请点这里
Heartbleed 的下载地址请点这里

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/97b1eec1eb7fada3c7882945db61bb84.html