Is the heartbleed bug a manifestation of the classic buffer overflow exploit in C?
Asked Answered
G

2

16

In one of our first CS lectures on security we were walked through C's issue with not checking alleged buffer lengths and some examples of the different ways in which this vulnerability could be exploited.

In this case, it looks like it was a case of a malicious read operation, where the application just read out however many bytes of memory

  1. Am I correct in asserting that the Heartbleed bug is a manifestation of the C buffer length checking issue?

  2. Why didn't the malicious use cause a segmentation fault when it tried to read another application's memory?

  3. Would simply zero-ing the memory before writing to it (and then subsequently reading from it) have caused a segmentation fault? Or does this vary between operating systems? Or between some other environmental factor?

  4. Apparently exploitations of the bug cannot be identified. Is that because the heartbeat function does not log when called? Otherwise surely any request for a ~64k string is likely to be malicious?

Gussi answered 15/4, 2014 at 16:56 Comment(3)
1. Yes. 2. It was the same program. 3. No. Owned by the server process, that's what caused the "bleeding". 4. No log of the keep-alive, which is where the I'm sending you 1000 bytes (now echo it back), but then I only send you 1 byte... hey look, you sent back 999 "other" bytes.Chalmer
Just to be clear, reading memory you don't own doesn't necessarily result in a segfault, it is simply undefined behavior.Koziarz
Why does the question suggest buffer overflow is unique to CBev
M
29

Am I correct in asserting that the Heartbleed bug is a manifestation of the C buffer length checking issue?

Yes.

Is the heartbleed bug a manifestation of the classic buffer overflow exploit in C?

No. The "classic" buffer overflow is one where you write more data into a stack-allocated buffer than it can hold, where the data written is provided by the hostile agent. The hostile data overflows the buffer and overwrites the return address of the current method. When the method ends it then returns to an address containing code of the attacker's choice and starts executing it.

The heartbleed defect by contrast does not overwrite a buffer and does not execute arbitrary code, it just reads out of bounds in code that is highly likely to have sensitive data nearby in memory.

Why didn't the malicious use cause a segmentation fault when it tried to read another application's memory?

It did not try to read another application's memory. The exploit reads memory of the current process, not another process.

Why didn't the malicious use cause a segmentation fault when it tried to read memory out of bounds of the buffer?

This is a duplicate of this question:

Why does this not give a segmentation violation fault?

A segmentation fault means that you touched a page that the operating system memory manager has not allocated to you. The bug here is that you touched data on a valid page that the heap manager has not allocated to you. As long as the page is valid, you won't get a segfault. Typically the heap manager asks the OS for a big hunk of memory, and then divides that up amongst different allocations. All those allocations are then on valid pages of memory as far as the operating system is concerned.

Dereferencing null is a segfault simply because the operating system never makes the page that contains the zero pointer a valid page.

More generally: the compiler and runtime are not required to ensure that undefined behaviour results in a segfault; UB can result in any behaviour whatsoever, and that includes doing nothing. For more thoughts on this matter see:

Can a local variable's memory be accessed outside its scope?

For both me complaining that UB should always be the equivalent of a segfault in security-critical code, as well as some pointers to a discussion on static analysis of the vulnerability, see today's blog article:

http://ericlippert.com/2014/04/15/heartbleed-and-static-analysis/

Would simply zero-ing the memory before writing to it (and then subsequently reading from it) have caused a segmentation fault?

Unlikely. If reading out of bounds doesn't cause a segfault then writing out of bounds is unlikely to. It is possible that a page of memory is read-only, but in this case it seems unlikely.

Of course, the later consequences of zeroing out all kinds of memory that you should not are seg faults all over the show. If there's a pointer in that zeroed out memory that you later dereference, that's dereferencing null which will produce a segfault.

does this vary between operating systems?

The question is vague. Let me rephrase it.

Do different operating systems and different C/C++ runtime libraries provide differing strategies for allocating virtual memory, allocating heap memory, and identifying when memory access goes out of bounds?

Yes; different things are different.

Or between some other environmental factor?

Such as?

Apparently exploitations of the bug cannot be identified. Is that because the heartbeat function does not log when called?

Correct.

surely any request for a ~64k string is likely to be malicious?

I'm not following your train of thought. What makes the request likely malicious is a mismatch between bytes sent and bytes requested to be echoed, not the size of the data asked to be echoed.

Menefee answered 15/4, 2014 at 17:44 Comment(5)
In regards to the last question, I would say any large echo request is malicious. It's consuming server resources (bandwidth, which costs money) to do something completely useless. There's really no valid reason for the heartbeat operation to support any length but zero.Tadashi
@R..: Had the designers of the API believed that then they would not have allowed a buffer to be passed at all, so clearly they did not believe that. There must be some by-design reason to support the echo feature; why it was not a fixed-size 4 byte buffer, which seems adequate to me, I do not know.Menefee
I disagree; IMO the designers were just incompetent. Nobody thinking from a security standpoint would think that supporting arbitrary echo requests is reasonable. Even if it weren't for the heartbleed overflow issue, there may be cryptographic weaknesses related to having such control over the content the peer sends; this seems unlikely, but in the absence of a strong reason to support a feature, a cryptographic system should not support it. It should be as simple as possible.Tadashi
64k strings: I'd read or heard that the max buffer length that could be returned using the exploit was 64k long, and assumed that any malicious user would want to get as much as possible. However your other parts of the answer suggest that being too greedy could in fact increase the risk of causing a fault, and therefore the best way of detecting an exploit is difference between actual buffer length and stated buffer lengthGussi
Nice answer, you may want to note Theo's comments that if they had not used a wrapper around malloc this would have resulted in a crash.Pottage
S
13

A segmentation fault does not occur because the data accessed is that immediately adjacent to the data requested, and is generally within the memory of the same process. It might cause an exception if the request were sufficiently large I suppose, but doing that is not in the exploiter's interest, since crashing the process would prevent them obtaining the data.

For a clear explanation, this XKCD comic is hard to better:

enter image description here

Sordino answered 15/4, 2014 at 18:13 Comment(2)
@chux Wouldn't 10 pictures be worth 10,000 words?Lollis
@Mike, true, the more common phrase is "A picture is worth 1,000 words." In writing documentation, I found the 10000:1 ratio more accurate, especially when I compare file sizes. :-)Subclimax

© 2022 - 2024 — McMap. All rights reserved.