Heartbleed in Action: A Buffer Overflow Vulnerability Affecting Countless Websites
Yesterday, a major security vulnerability named Heartbleed (CVE-2014-0160) was exposed in OpenSSL. Through the TLS heartbeat extension, it is possible to read up to 64 KB of memory on servers running HTTPS services, obtaining potentially sensitive information in memory. As this vulnerability has existed for two years, popular distributions such as Debian stable (wheezy) and Ubuntu 12.04 LTS, 13.04, 13.10, etc. are affected, and countless websites deploying TLS (HTTPS) are exposed to this vulnerability.
What is SSL heartbeat
SSL, or Secure Socket Layer, is a security layer based on TCP connections, providing authentication, encryption, and other functions. If you see a small green lock next to the URL when you are browsing, or the URL starts with https, or the URL bar has a green background, it means you are using SSL. Websites involving money or sensitive information generally use SSL secure connections.
Establishing an SSL connection requires two round-trip handshakes. The first handshake negotiates the encryption method used and obtains the server’s digital certificate. The second handshake negotiates the symmetric encryption key used for subsequent data transmission (like two people negotiating a common password). If the server is on the other side of the ocean, the delay in between can be several hundred milliseconds. Therefore, we hope to reuse established SSL connections as much as possible.
The problem is that we are not constantly clicking on web pages, so how does the server know if the person on the other end of the connection is still “alive”? If the person on the other end “dies”, the server needs to release resources as soon as possible, otherwise it will be filled with zombie connections. This requires the client to send a “heartbeat” every once in a while to tell the server “I’m still alive”. The server will also return a heartbeat to tell the client “I haven’t forgotten you”. This is the origin of the SSL heartbeat.
The SSL secure connection layer is divided into two layers: the lower layer is the record layer, which is unencrypted and can be captured with wireshark; the upper layer contains handshake information, key information, user data, heartbeat packets, etc., which are encrypted and packaged into individual “records”. Above the SSL secure connection layer is the familiar HTTP and other protocols. In other words, the SSL secure connection layer is transparently inserted between the application layer (such as HTTP) and the transport layer (such as TCP).
SSL heartbeat is parallel to the Handshake in the above figure. An SSL heartbeat consists of the following 4 fields:
- Heartbeat packet type: 1 byte, only request and response are possible.
- Payload length: 2 bytes, according to RFC 6520, it cannot exceed 2^14 = 16384, but OpenSSL’s implementation only checks on the client side that sends the request, and the server side does not check.
- Payload: as many bytes as the payload length, can be any byte. Whatever payload the request sends, the server should respond with the same payload, like an “echo”.
- Padding: at least 16 bytes, to fill an SSL record. The padding bytes of both the request and response should be random.
Vulnerability Analysis
The following vulnerability analysis is based on the OpenSSL-1.0.1e source code. TLS is a type of SSL transmission that is more secure than traditional SSL, so if both the client and server support it, it will be used preferentially. When the server receives a TLS heartbeat packet, it calls the tls1_process_heartbeat function in ssl/t1_lib.c (as shown below).
- Line 2490: p is initialized to point to the data area, the first byte (heartbeat packet type) is taken out and put into the hbtype variable.
- Line 2491: The next two bytes (payload length) are put into the payload variable. n2s is a macro that will increase p by 2. Note that there is no check on the payload length here.
- Line 2492: The subsequent bytes are considered to be payload and padding, assigned to pl, and will be sent out later.
Note, don’t confuse it with its “twin brother” dtls1_process_heartbeat. DTLS is a TLS based on UDP. Our websites generally use TCP for transmission, so tls1_process_heartbeat is called.
- Line 2499: If this is a heartbeat packet request…
- Line 2508: Allocate memory for the upcoming response.
- Line 2509: Use bp as the buffer pointer for the upcoming send.
- Line 2512: The first byte of the response is the heartbeat packet type.
- Line 2513: The 2nd and 3rd bytes of the response are the payload length.
- Line 2514: Copy the payload from the heartbeat packet request to the response buffer. The buffer overflow occurs here: if the “payload length” field (payload variable) is set very large by the sender, and the actual payload length is relatively short, the memory that originally does not belong to the payload area will be copied to the response buffer.
- Lines 2515~2517: Generate 16 random padding bytes and append them to the end of the payload.
- Then, the buffer pointed to by bp is loaded into the TLS record and sent to the client.
Without discussing whether there are any loopholes, I believe some people have already smelled the “bad smell” in the code. What do the variable names p, bp, pl mean? Maybe you can guess, but this naming method is really not flattering. Perhaps it is precisely because these codes are difficult to understand that bugs have a place to hide.
Exploiting the Vulnerability
From the previous analysis, it can be seen that this vulnerability is easy to exploit, and no additional conditions are required except for the OpenSSL version. There is no need to consider what kind of Web server software is installed on the server, whether there are files with special paths, etc. The effect is also very obvious: read up to 64 KB of server memory, and it will not leave traces in the server log. 64 KB is because the option of payload length is 16 bits, the maximum is 65535, which means that at most (64KB - 1) bytes of memory are leaked.
I wrote a POC (Proof Of Concept) code to verify. Since I am lazy, I will not study the SSL record layer format and SSL handshake troubles, and borrow the OpenSSL client library to increase the payload field of the heartbeat request. As shown in the figure below, modify line 2599 of ssl/t1_lib.c and replace the payload with a value not exceeding 65535 bytes. In this way, the real payload is only 18 bytes, that is, only the first 18 bytes in the returned response are consistent with the sent payload, and the nearly 64 KB behind are all server memory that should not be leaked.
The remaining part is to use the client library to complete the SSL handshake, send and receive the heartbeat. Since the modified tls1_heartbeat function is an unexported private function, it needs to be directly linked to the .o file. (For specific compilation commands, see the comments at the beginning of the POC code). I put the code on gist: POC code
This POC code can send normal HTTPS requests for testing, or it can send heartbeat requests after the SSL handshake is completed and wait for a response. If the server does not support the TLS heartbeat extension or has patched this vulnerability, it will not receive any response, and you need to press Ctrl+C to exit. If the server is recruited, it will output nearly 64 KB of server memory in a format similar to hexdump -C.
What Information Will Be Leaked
First, let’s warm up with CSDN. The SSL certificate is clearly visible. The following picture is the X.509 Subject part. Those \x are Chinese characters represented in UTF-8. Translated, it is the Chinese name of that company. Of course, it seems that there is no private key here, so the impact is not great.
How about our lovely 12306? Look, this brother is buying an adult ticket from Hohhot (telegram code HHC) to Xiongyue City (telegram code XYT) on April 8, 2014!
A company’s internal website leaked the nginx configuration.
Even Yahoo, such a big website, was not spared. The Cookie in the user’s HTTP request is clearly visible, and it can be used to impersonate other users to log in.
A comment just posted on someone’s blog (spam comment, haha).
What’s more interesting is that since the content of the server memory is constantly changing, the 64 KB content leaked each time the SSL heartbeat may be different.
USTC LUG has urgently upgraded all servers for OpenSSL to deal with this vulnerability.
Conclusion
RFC 6520 clearly stipulates that the length of the heartbeat packet cannot exceed 2^14 (16384). If the payload is too long, this heartbeat request should be discarded:
1 | The total length of a HeartbeatMessage MUST NOT exceed 2^14 or max_fragment_length when negotiated as defined in [RFC6066]. |
But the implementers of OpenSSL seem to have taken the restriction on the maximum payload length in RFC as a breeze (otherwise at most 16 KB instead of 64 KB of memory is leaked), and they fully trust the payload length sent by the client. Trusting user input in network programming is very dangerous, but such a clumsy bug has been hidden on millions of servers for as long as two years (this code was introduced at the end of 2011 and released with OpenSSL 1.0.1 in March 2012).
People from Microsoft’s Vulnerability Emergency Response Center once said that the security issues of software need to be investigated and tested by professionals. Just having enough eyeballs does not necessarily mean that bugs can be discovered. OpenSSL, as the underlying support for network security, should have enough eyeballs, but it has repeatedly exposed security vulnerabilities, which is worth our deep thought.
What is commendable is the process of discovering and handling this vulnerability: the discoverer of the vulnerability did not rush to make it public, but reported to large Internet companies and the maintenance teams of major Linux distributions through the CVE platform, allowing large websites that are related to the “life and death” of the Internet to patch up the vulnerability first, and Linux distributions also prepared patches. At the scheduled time, the vulnerability was made public to the world, and reports about this vulnerability were all over the Internet. At this time, ordinary users only need to apply the patches prepared by the Linux distribution to effectively defend against the vulnerability, which minimizes the harm of the vulnerability to the entire Internet.