2

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

据我所知,TLS 在 TCP 上运行。TCP 是面向流的,因此无法知道消息何时被传递。您必须提前知道传入消息应该多长时间或有一个分隔符来扫描。

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

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

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

4

2 回答 2

5

是心跳包的定义。

struct {
  HeartbeatMessageType type;
  uint16 payload_length;
  opaque payload[HeartbeatMessage.payload_length];
  opaque padding[padding_length];
} HeartbeatMessage;

不正确的payload_length字段处理是导致心脏出血错误的原因。

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

 struct {
      ContentType type;
      ProtocolVersion version;
      uint16 length;
      opaque fragment[TLSPlaintext.length];
  } TLSPlaintext;

结构 HeartbeatMessage 放置在上面的fragment.

所以当根据这里字段的数据到达时,可以处理一个完整的TLS“数据包” length,但是在内部Heartbeat消息中,openssl无法验证其payload_length.

这是一个数据包捕获的屏幕截图,其中您可以看到外部长度 3 指定了“数据包”的长度,而内部(错误)有效负载长度 16384 是导致漏洞利用的原因,因为 openssl 无法验证这一点实际接收到的数据包长度。

wireshark 心跳包截图

当然,在处理这个外部记录的字段时也必须注意类似的问题,在开始处理/解析数据包的内容之前length,您确实需要确保您确实收到length了数据。

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

于 2014-04-18T19:13:25.943 回答
1

Heartbleed 维基百科文章很好地解释了这个漏洞。换句话说,RFC 6520 是 TLS 协议的扩展,用于“心跳请求”消息(一种保持活动机制)。该请求由一个 16 位长度的字段和一个要匹配的消息组成,并且响应应该回显所提供的消息。OpenSSL 的实现有一个不执行边界检查的错误。它接受这个长度字段的表面值,而不检查它是否正在读取它不应该读取的内容(即超出 SSL 记录指示的边界)。当“心跳请求”格式错误,消息很小但长度字段中的值很大时,就会发生漏洞利用。这允许恶意客户端尝试从服务器中读取原本不会被读取的信息(此信息将在响应中返回)。

于 2014-04-18T18:46:17.010 回答