2

First, I am no C programmer and the OpenSSL codebase is huge, so forgive me for asking a question that I could probably find the answer to, given I had the time and skill to dig through the code.

TLS runs over TCP from what I can tell. TCP is stream oriented, so there is no way to know when a message has been delivered. You must know in advance how long the incoming message should be or have a delimiter to scan for.

With that in mind, how is it possible for OpenSSL to process a heartbeat request before the full payload has been received?

If OpenSSL just starts processing the first chunk of data it reads from the TCP socket after the payload length is received, then OpenSSL would appear to be not just insecure, but broken under normal operation. Since the maximum segment size of TCP is 536 bytes, any payload larger than that would span multiple TCP segments and therefore potentially span multiple socket reads.

So the question is: How/Why can OpenSSL start processing a message that is yet to be delivered?

Niels B.
  • 5,912
  • 3
  • 24
  • 44
  • This question belongs on another site in the Stack Exchange network. Perhaps security.stackexchange.com or crypto.stackexchange.com. – jww Apr 20 '14 at 08:20

2 Answers2

5

This is the definition of a heartbeat packet.

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

Incorrect handling of the payload_length field is what caused the heartbleed bug.

However this whole packet is itself encapsulated within another record that has it's own payload length, looking roughly like this:

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

The struct HeartbeatMessage is placed inside the above fragment.

So one whole TLS "packet" can be processed when the data according to the length field here has arrived, but in the inner Heartbeat message, openssl failed to validate its payload_length.

Here's a screenshot of a packet capture, in which you can see the outer length of 3 specifies the length of a "packet", and the inner (wrong) payload length of 16384 is what caused the exploit, as openssl failed to validate this against the actual received length of the packet.

wireshark screenshot of heartbeat packet

Ofcourse, similar care must be taken when processing the length field of this outer record, you really want to make sure you have actually received length data before beginning to process/parse the content of the packet.

Note also that there's not a particular correlation between socket reads and TCP segments, 1 socket read can read many segments, or just part of a segment. To the application, TCP is just a byte stream, and one socket read could read just up to half the length field of one TLSPlaintext packet, or it could read several whole TLSPlaintext packets.

Community
  • 1
  • 1
nos
  • 223,662
  • 58
  • 417
  • 506
1

The Heartbleed Wikipedia article explains the exploit quite well. To paraphrase, RFC 6520 is an extension to the TLS protocol for a "Heartbeat Request" message (a kind of keep-alive mechanism). The request consists of a 16 bit length field and a message to match, and the response is supposed to echo the provided message. OpenSSL's implementation has a bug that does not perform bounds checking. It accepts this length field at face value without checking to see if it is reading into something it shouldn't be (i.e., beyond the boundary indicated by the SSL Record). The exploit occurs when the "Heartbeat Request" is malformed with a small message but a large value in the length field. This allows a malicious client to attempt to read information out of the server that would otherwise not have been read (this information would come back in the response). What is actually in that information depends on how things got stored into memory on the server, but the potential for reading sensitive information is considered catastrophic for OpenSSL, which is supposed to provide a secure platform.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • But, shouldn't the receiving OpenSSL of malformed request wait until it has received all the bytes as indicated by the length field? What is preventing OpenSSL from waiting to read as many bytes as found in the length field? One TCP recv() does not guarantee that the entire packet is received, right? – Prabhu Apr 18 '14 at 18:55
  • Exactly prabhu. I don't think jxh answers the question here. TCP does not operate with any concept of "packets". It's just a data stream. – Niels B. Apr 18 '14 at 18:55
  • 1
    @Prabhu: The Heartbeat Request message is like any other SSL message, it is encapsulated within an SSL Record. Processing the SSL Record reveals the Heartbeat Request, which is interpreted erroneously. The fix is to check to make sure the Heartbeat Request length does not extend beyond the SSL Record length. – jxh Apr 18 '14 at 20:50