Heartbleed bug:为什么在交付有效载荷之前甚至可以处理心跳请求?

Nie*_* B. 2 c sockets tcp heartbleed-bug

首先,我不是C程序员,OpenSSL代码库是巨大的,所以请原谅我提出一个我可以找到答案的问题,因为我有时间和技巧来挖掘代码.

从我所知道的,TLS在TCP上运行.TCP是面向流的,因此无法知道消息何时被传递.您必须事先知道传入消息应该有多长或者要有扫描的分隔符.

考虑到这一点,OpenSSL如何在收到完整的有效载荷之前处理心跳请求?

如果OpenSSL刚刚开始处理在收到有效负载长度后从TCP套接字读取的第一个数据块,那么OpenSSL似乎不仅不安全,而且在正常操作下会中断.由于TCP的最大段大小为536字节,因此任何大于该值的有效负载将跨越多个TCP段,因此可能跨越多个套接字读取.

所以问题是:OpenSSL如何/为什么开始处理尚未交付的消息?

nos*_*nos 5

是心跳包的定义.

struct {
  HeartbeatMessageType type;
  uint16 payload_length;
  opaque payload[HeartbeatMessage.payload_length];
  opaque padding[padding_length];
} HeartbeatMessage;
Run Code Online (Sandbox Code Playgroud)

错误的payload_length字段处理是造成心脏病的原因.

但是,整个数据包本身都封装在另一个具有自己的有效负载长度的记录中,大致如下所示:

 struct {
      ContentType type;
      ProtocolVersion version;
      uint16 length;
      opaque fragment[TLSPlaintext.length];
  } TLSPlaintext;
Run Code Online (Sandbox Code Playgroud)

结构HeartbeatMessage放在上面fragment.

因此,当根据length此处的字段的数据到达时,可以处理一个完整的TLS"数据包" ,但是在内部Heartbeat消息中,openssl无法验证它payload_length.

以下是数据包捕获的屏幕截图,其中您可以看到外部长度为3指定"数据包"的长度,内部(错误)有效负载长度为16384是导致漏洞利用的原因,因为openssl未能对此进行验证数据包的实际接收长度.

wireshark心跳包的截图

当然,在处理length这个外部记录的字段时必须小心谨慎,你真的想确保length在开始处理/解析数据包内容之前确实已经接收过数据.

另请注意,套接字读取和TCP段之间没有特定的相关性,1个套接字读取可以读取许多段,或者只是段的一部分.对于应用程序,TCP只是一个字节流,一个套接字读取只能读取一个TLSPlaintext数据包的长度字段的一半,或者它可以读取几个完整的TLSPlaintext数据包.