Wednesday, April 09, 2014

Why heartbleed doesn't leak the private key [retracted]

I got this completely wrong!

So as it turns out, I completely messed up reading the code. I don't see how, but I read it one way. I can still visualize the code in my mind's eye that I thought I read -- but it's not the real code. I thought it worked one way, but it works another way.

Private keys are still not so likely to be exposed, but still much more likely than my original analysis suggested.

The incorrect post is below, so you know how wrong I was

The headline for #heartbleed is that it might leak the private key. In most software, this cannot happen. That's because memory containing the private key is never freed, and hence allocated heartbleed buffers can never contain it.

SSL hearbeats are a simple echo service. You send data (up to 64k) in a request, the server copies that data into a buffer, and then sends it back to you as the response. The vulnerability is that that there are two "length" fields in the request. You make one claim there is 64k of data, but the other claims 0, and you send 0. On the server side OpenSSL allocates a buffer according to the size of the first number, but copies the amount of data according to the second number -- thus changing none of the bytes in the buffer. What it sends is whatever happens to be in the allocated memory before hand.

The memory is allocated using "malloc()" from the "heap". OpenSSL is a loaded as a library, so does not have its own "heap", but shares that heap with the application. Thus, almost anything from the hosting application is fair game for being disclosed via heartbleed. The only requirement is that the information must at some time have been freed.

Memory is allocated from a "heap" using "malloc". Apparently, OpenSSL has it's own heap separate from the application. Thus, in theory, things inside the host application cannot leak through heartbleed. I don't think this is completely true: looking at bled buffers, I see data that looks like it came from the host application and not OpenSSL. That may be because the host application calls "OPENSSL_malloc()" instead of the normal "malloc()" to allocate memory. In any event, the major requirement for information to leak is that it must at some point have been freed inside the right heap.

One issue with "malloc()" is that it prefers recently freed information. Memory that is allocated is almost always the memory that was most recently freed. What that means for private keys is that even if you free the underlying memory, it's almost always going to be immediately re-allocated for some other purpose and overwritten. Thus, the server may leak the private key right after a reboot, but not later.

The upshot is this. What you can eavesdrop on with heartbleed hacks is dynamic stuff, stuff that was allocated only moments ago. What you probably can't get is static information. Certainly, you can't get any static information that hasn't been freed, and you probably can't get static information that was freed long ago, such as at program startup. It's a great way to steal passwords from recent logins, but it's unlikely to give private keys.

Now, this only applies to software packages in general. Certainly, there is some poorly written software that when it validates the SSL connection, copies the private key into a buffer, uses it, then frees the buffer. Thus, there certainly exists some software that reliably leaks the private key, it's just that on most software it's not possible.

Note: By the way, the major thing that is allocated/freed is the decrypted message buffer -- meaning your HTTP traffic on top of SSL. That means the major thing that leaks is HTTP headers -- namely session cookies and login passwords. Thus, while your SSL certificates are likely safe, your passwords aren't.


Mikko K said...
This comment has been removed by the author.
Mikko K said...

You might want to try this challenge:

Daniel Franke said...

I think this post is completely mistaken. The two length fields in a TLS heartbeat request are the length of the TLS record, and the length of the payload that the requestor wants echoed back in the response. The bug occurs when the (claimed) payload length is longer than the record length. It seems to be your impression that if the attacker sets a payload length of 65535 bytes but only sends 1 byte of actual payload, then OpenSSL will allocate a 65535 bytes, copy 1 byte, and leave the rest of the destination buffer uninitialized. That's not the case. It will allocate 65535 bytes and copy 65535 bytes, therefore reading off the end of the source buffer into whatever comes next on the heap. There's no guarantee whatsoever as to whether or not what comes next on the heap is leftover freed data.

Ivan Pepelnjak said...

@Daniel: please excuse my ignorance, but if OpenSSL does allocate 64K buffer and send it back, then nothing that was allocated at the moment of the probe could be leaked, right?

If it would allocate 1 byte and copy 64K bytes, then it could obviously access other currently allocated data structures, and I would suspect this might be the case.

So what's actually going on?

No One said...

Your analysis is not even wrong.