Click to See Complete Forum and Search --> : stop buffer overflows from becoming a security risk


Chess007
07-20-2006, 06:44 PM
This idea sounded like a great way to stop the security issues that are associated with buffer overflows. I'm looking for some opinions on it.

Leo: But he did have a question. He said: When it comes to the stack, why did it have to write up? Wouldn’t it be more secure to write down? Or is there something more complicated going on with that?

Steve: Okay, now, I didn’t quite get his question.

Leo: He’s talking about why does it go up in memory instead of down in memory. But I don’t think it makes any difference, does it?

Steve: Well, actually there’s something brilliant about this.

Leo: Oh.

Steve: Which I really liked. First of all, he says, why does it write up, meaning why does your – when you’re overflowing the buffer, you’re going from lower memory to higher memory.

Leo: Right.

Steve: And that’s writing up the stack, and that’s overwriting the stuff that’s higher in the stack, which is critical information from previous programs or previous subroutines. Because as we remember from last week, the stack grows downwards. Well, his point was, if buffers overflowed the other direction, then they wouldn’t be overwriting the previous information, they’d just be writing – they’d be overwriting stuff that wasn’t allocated yet on the stack. Well, he’s completely correct. Now, of course...

Leo: So why don’t they do it that way?

Steve: Exactly. I mean, they should. I mean, I love the question. It’s brilliant.

Leo: Wait a minute. Wait a minute. Now, come on. You’re turning years of computer science on its head. Literally.

Steve: Well, okay, now. First of all, it’s not easy from a practical standpoint to fill buffers downward. That is, you know, just everything about the way we think has a buffer being filled from lower memory to higher memory.

Leo: But even if you did that, you would still overwrite – you could overwrite in the negative numbers.

Steve: Well, but hold on. But if the stack instead started at the bottom and grew upwards...

Leo: Yeah.

Steve: ...and there’s no reason it can’t, if the stack started at the bottom and grew upwards...

Leo: So you’re saying if it started at memory location zero...

Steve: Or, well, zero, not everybody can start at zero.

Leo: Not really zero.

Steve: Yeah.

Leo: Let’s say whatever zero is, some arbitrary zero.

Steve: Yes. And then allocating memory on the stack you do by moving your stack pointer up to, like, a certain amount. Now, what’s happened is you’ve reserved all of that memory below that point for your own use. Anybody else who wants some, they move the pointer up, and now they have – they’ve reserved that region that they just moved the pointer across for their own purpose. And the beauty of this is, the person who is currently using the stack, so-called is like on the top of the stack. And if stacks grew from the bottom up instead of from the top down, then buffer overruns would overrun out into unused stack space, not already used stack space. It’s brilliant. I mean, it would really work. And it would solve the problem.

Leo: Somehow I think there’s more to it than that, but all right. If it were that simple, wouldn’t somebody have done this?

Steve: No. Because, again – no, I mean, I...

Leo: All of this was set up long before this ever became a problem.

Steve: Exactly. That’s exactly it, Leo, is that it’s a little more elegant from an architectural standpoint to have, for reasons that are sort of complicated and deal with the way pointers are handled, to have the stack growing down from the top of memory is architecturally simpler. But it was just done that way basically as an arbitrary choice. Someone said, shall we have it come down from the top or up from the bottom? And it’s a little simpler to have it come down from the top. But in terms of the problems that it creates, the idea that overrunning the allocation, if you’re coming down from the top, you’re inherently overwriting the information of someone who’s already allocated information on the stack before you.

Leo: Right. Somebody else’s stack frame.

Steve: Somebody else’s stack, exactly. But literally, if you allocate instead from the bottom up, you don’t overrun. It’s wonderful.

Source:
http://www.grc.com/sn/SN-040.htm

My question is, would that work? Is it possible? Thoughts/opinions?

bwkaz
07-20-2006, 07:20 PM
Oh, great, Steve Gibson...

Well, here are a few problems with that. First, several people have already written software that will prevent stack-smashing attacks. Basically, they make the stack (and the heap, for that matter) non-executable. grsecurity is the first one that I used, but I don't think they were the first ones to do this (I think it originated somewhere in BSD land). I think SELinux also has something that will do it, but I'm not sure on that. Even XP SP2 has it as an option (though it's only turned on for "critical system processes" by default, and the implementation is too brain-dead to actually be foolproof without extra hardware support that's (AFAIK) only in x86-64 processors).

Second, it won't actually (completely) fix the problem. Sure, you're not overwriting other functions' stacks. And you're not overwriting your own return address. But you are still overwriting stuff: if a function declares two buffers on the stack, then overflows the first, the attack can still overwrite the second. (This would actually introduce code execution if the second buffer was a COM vtable, or a C++ object with virtual functions, or anything else where the user calls through pointers stored in the buffer. When the function calls through one of those overwritten pointers, it can call off into the unused portion of the stack (which was also overwritten), just like a current stack-overflow attack does.)

Third, it actually makes things slightly worse. When you have a stack overflow today, then when the buffer gets overflowed, it'll either crash the process or run arbitrary code, depending on what it got overflowed with. If the stack grew the other way, then when the stack overflow was triggered, it would either run arbitrary code (see above) or do nothing. Personally, I'd rather have an indication that something was wrong (i.e. the process crash), if random code does not get executed; that way, I can at least start looking more closely at a certain area of code when trying to fix the bug. With a reverse stack, I wouldn't even know I had a bug, let alone be fixing it. But an attacker could still find the pattern in the source or the binary that indicates the buffer overflow, and they could create an exploit.

(In that respect, this suggestion is rather like the suggestion to close source to keep bugs from being discovered. It doesn't actually hurt the people looking for bugs to exploit, but it does hurt the people that try to fix those bugs.)

cybertron
07-20-2006, 09:12 PM
I hate it when I do something illegal with memory but my app doesn't crash until later. Not that I just had that happen an hour ago.;)

voidinit
07-21-2006, 12:31 AM
Oh, great, Steve Gibson...



Whole heartedly agree with that sentiment. Gibson (http://en.wikipedia.org/wiki/Steve_Gibson) carries about as much weight with my mind share as DiDio (http://en.wikipedia.org/wiki/Laura_DiDio).

Speaking of GRSecurity and if anyone is interested. I recently ported Brad's 2.1.9 GRSec patch to Xen 3.0.2-3 (2.6.16.13 kernel). So both the host and paravirualized guests can use it. For the time being, only x86_64 architecture works, and getting the global_gdt_descr straightened out on i386 is over my head, although the PAX Team says they will do it once Xen is mainline.

More on this here. (http://forums.grsecurity.net/viewtopic.php?t=1490&start=0)

Chess007
07-21-2006, 05:00 AM
Thanks guys, thanks for explaining. :) It seems there are no silver bullets. Once again I have been slapped by penguins. :)

Wouldn't it be possible to make a program that would look for buffer overflows in code? So that they could be fixed? Or is it a given that because programs are so big, they will have buffer overflows?

bwkaz
07-21-2006, 06:59 PM
You can look for them, yes, but I'm not sure what exactly you'd be looking for. I know there are certain patterns of machine-language instructions that usually indicate some kind of overflow, but those are going to be different from CPU to CPU and from OS to OS (and probably even from compiler to compiler), and I don't know what they are for sure, either. You as a person can recognize it when you see it (if you know more or less what to look for), but the recognition process may be AI-hard. (Or maybe not, I don't know for sure.)

Plus, I'm not sure whether you'd be able to tell where in the source code the overflow is happening, to fix it. With debug info you could, but without it... not so sure. It would depend on whether the function names are stored in the executable anywhere. Now if you have the source, you can just run the scanner on a debug build, but if you don't, you may not be able to use the scanner at all.

Besides, more and more code is being released every single day; you'd have an awfully huge task if you wanted to scan everything... :eek:

cybertron
07-21-2006, 09:38 PM
I know there were some automated tools to analyze code for possible vulnerabilites, but they admit that they don't catch everything. Basically it was stuff like unchecked strcpy's or something. Wouldn't work on a compiled app either from what I understand - they were all being run on open source software for the sake of comparing the security of various apps (which was actually a bad idea itself, but it sounds like the same basic idea we're discussing here).