Heartbleed Vulnerability |
|
The Heartbeat message resides in the ssl3.h header source code file and is a foundation for the SSL communications to occur. This A more technical illustration of the communication between the client and the server. The length is the number of bytes and the data is the pointer to the Heartbeat message.
The length is the number of bytes in the received in the heartbeat message but the there is a payload_length that describes the number of bytes in the arbitrary payload that has to be sent back. The problem is the payload is never vetted against the data. This is illustrated in the diagram below (figure 2) (Williams, 2014).
The client initiates the message and therefore controls the payload length. The code is relying on data outside the program to control its behavior.
So the message type is popped into the hbtype variable, the pointer is incremented by one byte, and the n2s() macro writes the 16-bit length of the heartbeat payload to the variable payload and increments the pointer by two bytes. Then pl becomes a pointer to the contents of the payload.
Let's say a heartbeat message with a payload_length of 65535, ie: a heartbeat with a 64KB payload, the maximum possible, is received. The code has to send back a copy of the incoming HeartbeatMessage, so it allocates a buffer big enough to hold the 64KB payload plus one byte to store the message type, two bytes to store the payload length, and some padding bytes, as per the above structure. It constructs the reply HeartbeatMessage structure with the following code, where bp is a pointer to the start of the reply HeartbeatMessage:
Let's say a heartbeat message with a payload_length of 65535, ie: a heartbeat with a 64KB payload, the maximum possible, is received. The code has to send back a copy of the incoming HeartbeatMessage, so it allocates a buffer big enough to hold the 64KB payload plus one byte to store the message type, two bytes to store the payload length, and some padding bytes, as per the above structure. It constructs the reply HeartbeatMessage structure with the following code, where bp is a pointer to the start of the reply HeartbeatMessage:
So the code writes the response type to the start of the buffer, increments the buffer pointer, uses the s2n() macro to write the 16-bit payload length to memory and increment the buffer pointer by two bytes, and then it copies payload number of bytes from the received payload into the outgoing payload for the reply.
In the casae of the heartbleed bug, the payload is controlled by the hacker and it’s large at 64KB. If the actual heartbeat message sent by the attacker only has a payload of 1 byte for example and its payload_length is a masquerade, the above memcopy() will read beyond the end of the received message and start reading from the target’s memory.
And this memory will contain other juicy information, such as passwords or decrypted messages from other clients. Sending another heartbeat message leaks another 64KB, so rinse and repeat to scour the victim's system for goodies.
In fact, the bug leaks this sort of information, although it is believed Yahoo! has since patched its systems. This was a wireshark output that someone had posted showing evidence that decrypted passwords were passed through the network. The orange highlights show the redacted passwords from the screen shots.
In the casae of the heartbleed bug, the payload is controlled by the hacker and it’s large at 64KB. If the actual heartbeat message sent by the attacker only has a payload of 1 byte for example and its payload_length is a masquerade, the above memcopy() will read beyond the end of the received message and start reading from the target’s memory.
And this memory will contain other juicy information, such as passwords or decrypted messages from other clients. Sending another heartbeat message leaks another 64KB, so rinse and repeat to scour the victim's system for goodies.
In fact, the bug leaks this sort of information, although it is believed Yahoo! has since patched its systems. This was a wireshark output that someone had posted showing evidence that decrypted passwords were passed through the network. The orange highlights show the redacted passwords from the screen shots.
The fix
The code fix has two tasks to perform; (Chandra, 2014)
/* Read type and payload length first */
If (1 + 2 + 16 > s->s3->rrec.length)
return 0; /*silently discard
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec .4 */
pl = p;
First, it checks to determine if the length of the payload is zero or not. It simply discards the message if the payload length is 0 as shown below
If (1 + 2 + 16 > s->s3->rrec.length)
return 0; /*silently discard
The second task performed by the bug fix makes sure that the heartbeat payload length field value
matches the actual length of the request payload data. If not, it discards the message. The code excerpt
that performs this task as shown below: Heartbeat payload actual length check.”
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec .4 */
Code that is dependent on the properties of the data
Very similar to what we saw in the discussion group in the class, this piece of code relies on the property of the data that is enforced outside the code. In this case the file asn_mime.c uses the following code to toggle to lower case characters to ensure the buffer is set properly.
The code fix has two tasks to perform; (Chandra, 2014)
/* Read type and payload length first */
If (1 + 2 + 16 > s->s3->rrec.length)
return 0; /*silently discard
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec .4 */
pl = p;
First, it checks to determine if the length of the payload is zero or not. It simply discards the message if the payload length is 0 as shown below
If (1 + 2 + 16 > s->s3->rrec.length)
return 0; /*silently discard
The second task performed by the bug fix makes sure that the heartbeat payload length field value
matches the actual length of the request payload data. If not, it discards the message. The code excerpt
that performs this task as shown below: Heartbeat payload actual length check.”
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec .4 */
Code that is dependent on the properties of the data
Very similar to what we saw in the discussion group in the class, this piece of code relies on the property of the data that is enforced outside the code. In this case the file asn_mime.c uses the following code to toggle to lower case characters to ensure the buffer is set properly.
The code Is so complex that a programmer cannot accurately predict its behavior
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
This doesn’t make any sense. First, if there are 2 separate values to be added, then they should have been defined as constants, possibly in the main() as global variables. Especially if this is allocating memory in the heap. This would allow them to change the values in one location, otherwise to update the values the programmer would need to search throughout all lines of code to update, and possibly miss one. Second, if there is no real meaning from the first value “1” to the second “2”, this should be collapsed into a single value. Moreover, the entire expression should probably be defined as a single variable higher in the code. The function is allocating memory, the more unexplained variables the higher the likelihood of an overflow or other vulnerability.
The code snippets below show variables names that are very hard to interoperate.
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
This doesn’t make any sense. First, if there are 2 separate values to be added, then they should have been defined as constants, possibly in the main() as global variables. Especially if this is allocating memory in the heap. This would allow them to change the values in one location, otherwise to update the values the programmer would need to search throughout all lines of code to update, and possibly miss one. Second, if there is no real meaning from the first value “1” to the second “2”, this should be collapsed into a single value. Moreover, the entire expression should probably be defined as a single variable higher in the code. The function is allocating memory, the more unexplained variables the higher the likelihood of an overflow or other vulnerability.
The code snippets below show variables names that are very hard to interoperate.
Overall, there are a number of examples of bad coding practices like single character variable names and the use of hard-coded constants throughout the source code that would make this very difficult to know how the program will behave. Additionally, debugging this would be equally difficult.
What could be done in the future to catch the next Heartbleed?
Several post mortem activities have been carried out against the Heartbleed bug was discovered. David Wheeler, PhD. Was one who conducted a thorough analysis of the bug and described several areas in testing that may or may not have made a difference in the outcome of the implementation of the release.
A key lesson to be learned is that the static and dynamic analysis approaches often used by many projects today cannot find problems like Heartbleed. This includes mostly-positive automated test suites, common fuzz testing approaches, and typical statement or branch code coverage approaches. Several source code weakness analyzer developers are improving their tools to detect vulnerabilities very similar to Heartbleed, and that is good news. But it is obvious that this is not enough. (Wheeler, 2017)
Projects should make sure that they are easier to analyze. For example, they should simplify their code, simplify their Application Program Interface (API) and allocate and deallocate memory normally.
There are also many ways to reduce the impact of Heartbleed-like vulnerabilities that should be considered:
What could be done in the future to catch the next Heartbleed?
Several post mortem activities have been carried out against the Heartbleed bug was discovered. David Wheeler, PhD. Was one who conducted a thorough analysis of the bug and described several areas in testing that may or may not have made a difference in the outcome of the implementation of the release.
A key lesson to be learned is that the static and dynamic analysis approaches often used by many projects today cannot find problems like Heartbleed. This includes mostly-positive automated test suites, common fuzz testing approaches, and typical statement or branch code coverage approaches. Several source code weakness analyzer developers are improving their tools to detect vulnerabilities very similar to Heartbleed, and that is good news. But it is obvious that this is not enough. (Wheeler, 2017)
Projects should make sure that they are easier to analyze. For example, they should simplify their code, simplify their Application Program Interface (API) and allocate and deallocate memory normally.
There are also many ways to reduce the impact of Heartbleed-like vulnerabilities that should be considered:
- Enable memory allocator defenses once a standard memory allocator is used instead. Some systems, like GNU malloc on Linux, do not really have such mechanisms, so they would need to be added first.
- Overwrite critical information (like passwords and private cryptographic keys) whenever you’re done with it.
- Make perfect forward security (PFS) encryption algorithms the default.
- Use privilege separation to separate the critical cryptographic secrets from the rest of the code.
- Fix the SSL/TLS certificate infrastructure, especially the default certificate revocation process. This will require concerted effort to get a real solution (such as X.509 OCSP must-staple) specified, implemented, and widely deployed; we need to start now.
- Make it easy to update software.
- Store passwords as salted hashes.
- General issues: Secure software education/training and reduce attacker incentives.
References
Chandra, B., May 13th, 2014., A technical view of the OpenSSL ‘Heartbleed’ vulnerability, A look at the memory leak in the OpenSSL Heartbeat implementation. Version 1.2.1.
Borges, A., April 2014, How to perform a Heartbleed Attack. Revision A.1
Wheeler, D., January 2017, How to Prevent the next Heartbleed
Williams, C., April 2014, Anatomy of OpenSSL’s Heartbleed: Just four bytes trigger horror bug
Limer, E., April 2014, How the Heartbleed Works: The code behind the Internet’s Security Nightmare
Agrawal, A., Heartbleed bug – the source code. Retrieved March 20, 2017. https://amanagrawal.blog/2014/04/09/heartbleed-bug-the-source-code/
Mutton, P., April, 2014, Half a million widely trusted websites vulnerable to Heartbleed bug. Retrieved March 20, 2017. https://news.netcraft.com/archives/2014/04/08/half-a-million-widely-trusted-websites-vulnerable-to-heartbleed-bug.html
Chandra, B., May 13th, 2014., A technical view of the OpenSSL ‘Heartbleed’ vulnerability, A look at the memory leak in the OpenSSL Heartbeat implementation. Version 1.2.1.
Borges, A., April 2014, How to perform a Heartbleed Attack. Revision A.1
Wheeler, D., January 2017, How to Prevent the next Heartbleed
Williams, C., April 2014, Anatomy of OpenSSL’s Heartbleed: Just four bytes trigger horror bug
Limer, E., April 2014, How the Heartbleed Works: The code behind the Internet’s Security Nightmare
Agrawal, A., Heartbleed bug – the source code. Retrieved March 20, 2017. https://amanagrawal.blog/2014/04/09/heartbleed-bug-the-source-code/
Mutton, P., April, 2014, Half a million widely trusted websites vulnerable to Heartbleed bug. Retrieved March 20, 2017. https://news.netcraft.com/archives/2014/04/08/half-a-million-widely-trusted-websites-vulnerable-to-heartbleed-bug.html