OpenSSL的Heartbleed漏洞原理及简单模拟

转自  http://www.myhack58.com/Article/60/63/2014/44763.htm

Heartbleed漏洞
自从Heartbleed漏洞曝光以来,网上能看到很多相关的文章,但大部分都是写的云里雾里,本文尝试直观明了的对漏洞原理进行说明及模拟。
 
OpenSSL是SSL协议以及一系列加密算法的开源实现,使用C语言编写。OpenSSL采用Apache开源协议,可以免费用于商业用途,在很多linux发行版和服务器中得到广泛应用。OpenSSL出现漏洞造成的影响是巨大的,Heartbleed漏洞被称作“历史上最为严重的网络安全漏洞”。 
Heartbleed漏洞最先由国际网络安全研究和一位Google研发人员在4月3日独立发现。但直到4月7日,OpenSSL1.0.1g版本发布修复这个漏洞,漏洞信息才得以公开。这也是防止补丁发布之前大批黑客得知这个漏洞并进行攻击。而自2011年12月31日,漏洞就已经存在,已经有报道表明漏洞消息发布之前曾有黑客利用Heartbleed进行攻击。
 
漏洞原理
TLS位于传输层和应用层之间,提供数据安全加密。TLS心跳指的是用户向服务器发送数据包,服务器返回一个相同的数据包以确定彼此在线,以支持持续通信功能。
 
用户向服务器发送的心跳数据中用两个字节表明有效负载数据长度,而服务器端OpenSSL将根据这个有效负载长度构造一个新的数据包会送给对端。
 
简单的说,服务器端得到数据包,数据包长度为plen_real,而数据包中包含一个字节表明有效负载数据长度plen_fake,数据包剩下的部分是有效负载数据,长度为plen_real-1。整个数据包存储在一个char型数组之中。而服务器端构造新数据包时,先分配一段plen_fake+1的内存空间,前两个字节存放plen_fake,之后使用memcpy从收到的数据包有效负载数据起始位置向新数据包拷贝plen_fake字节数据。正常情况下plen_fake = plen_real-1,当用户有意设置plen_fake大于实际有效负载长度plen_real-1时,服务器就会发送plen_fake长度的数据,其中包括plen_fake - plen_real-1长度的数据,这些数据可能是一些用户密码或者密钥。
 
#include<malloc.h>
#include<stdio.h>
#include<string.h>
 
#define plen_real 5 //获得的数据包长度
int main(){
  char other1[100] ;memset(other1,'E',100);
  char package_send[plen_real ] = {0xF,'d','a','t','a'};//获得的数据包
  char other2[100] ;memset(other2,'E',100);
  char* pdata = package_send+1;
  int plen_fake = package_send[0];
  char* package_return = (char*)malloc(plen_fake +1 );//新数据包
  memcpy(package_return+1, pdata, plen_fake);
  
  printf("package data send:\n");
  for(int i = 1;i<plen_real;i++) printf("%c",package_send[i]);
  printf("\n");
  
  printf("package data send back:\n");
  for(int i = 1;i<plen_fake +1;i++) printf("%c",package_return[i]);
  printf("\n");
  
  return 0;
}
 使用 ideone.com  在线编译器,选择c99标准,输出结果为:
package data send:
data
package data send back:
dataEEEEEEEEEEE
可以看到服务器返回了不当信息,可能包括用户密码、密钥等敏感信息。
上述描述为了简化,省去了数据包中的padding、type等字段信息,但原理是一样的。而有效负载数据长度plen_fake其实是用两个字节表示的,2^16 = 64k,这也是为什么最多会64kB泄露数据。
 
漏洞修复
修复方法很简单,只要在生成返回数据包之前检查plen_fake是否正确即可。
 
OpenSSL补丁采用的方式是如果plen_fake大小不对,默认此数据包为黑客发起的攻击,直接return不做回应。例如:
#include<malloc.h>
#include<stdio.h>
#include<string.h>
 
 
#define plen_real 5
int main(){
  char other1[100] ;memset(other1,'E',100);
  char package_send[plen_real ] = {0xF,'d','a','t','a'};
  char other2[100] ;memset(other2,'E',100);
  char* pdata = package_send+1;
  int plen_fake = package_send[0];
 
  //出错则认为用户恶意操作,不做应答
  if(1 > plen_real ) return 1;
  if(1 + plen_fake > plen_real ) return 1;
 
 
  char* package_return = (char*)malloc(plen_fake +1 );
  memcpy(package_return+1, pdata, plen_fake);
  
  printf("package data send:\n");
  for(int i = 1;i<plen_real;i++) printf("%c",package_send[i]);
  printf("\n");
  
  printf("package data send back:\n");
  for(int i = 1;i<plen_fake +1;i++) printf("%c",package_return[i]);
  printf("\n");
  
  return 0;
}
 
OpenSSL补丁代码修改在这里。
 
SSL参考链接:
1、深入研究SSL协议
 

相关推荐