OpenSSL has been in the news as of late, owing to a vulnerability in the way that it handles data provided to it in the context of the Heartbeat Extension.
It’s called Heartbleed, because the extension “bleeds” data when given a little encouragement.
Now, there are countless articles available that describe the issue, but little in the way of discussing the root cause of the problem.
To recap; OpenSSL 1.0.1 before 1.0.1g allows a remote attackers to obtain sensitive information from process memory by sending specially “crafted packets” that trigger a buffer over-read.
The heartbeat is provided during the negotiation stage of the SSL/TLS handshake.
The point of the heartbeat is to provide continuous data transfer to ensure that there is no timeout.
The immediate cause of the issue is a lack of checking that the stated payload length in the request for a heartbeat is the same the actual length of the heartbeat message.
The stated length is used to allocate (malloc) memory for the data that will be sent by the requester.
The issue occurs because the requestor provides a large number (up to 65K) in the stated message length, but only provides a small (or even 0) length message. The memory is not wiped prior to writing, is then read, and provided back as a response. Any data previously stored in the read memory is provided, and has been reported to include:
- Primary key
- Secondary key material (including user names and passwords)
- Protected content (including the content of the materials being sent over the SSL/TLS tunnel)
- Collateral data associated with implemented security measures
Pretty simple huh?
So, what is the programmatic solution?
Well, we could wipe the memory following the call to malloc().
Or we could validate the input.
I raise this point input validation is an issue that is present in so much web application software; typically in the context of injection vulnerabilities, cross site scripting, and the like. Here we see it again, in an obscure protocol handling implementation.
What is the solution?
Here is a possible check:
if (1 + 2 + payload + 16 > s->s3->rrec.length)
Another option of course, would be to zero the memory during allocation (calloc rather than malloc), or immediately after allocation:
memset (bp, 0, 1+2+payload+16);
So, the root of the problem again?
Adequate consideration of input values and what might occur if the values are outside of the bounds of what we expect.
This issue is just another example of the importance of applying secure coding techniques for all software development.
As the Heartbleed issue demonstrates, this stuff really matters.
Joel Weisz, QSA
Managing Consultant – Security Assessments