PDA

Bekijk Volledige Versie : Buffer overflow prevention



Eygene A. Ryabinkin
13/08/03, 20:15
Hi!
I have an idea on buffer overflow prevention. I doubt that it's new, but I
haven't seen an implementation of it in any freely distributable Un*x system.
So, I hardly need your comments on it.

Preliminary: I'm talking about Intel x86 architecture, but maybe it will be
applicable to others as well.

The idea itself: all (correct me if I'm wrong) buffer overflows are based on
the fact that we're using the stack, referenced by SS:ESP pair, both for
procedure return address and for local variables. It seems to me, that would we
have two stacks -- one for real stack and one for variables -- it will solve
a bunch of problems. So, my suggestion: let us organise two segments: one for
normal stack, growing downwards, referenced by SS:ESP pair and the second one,
for local variables, referenced by GS:EBP pair, with either upwards or
downwards growing. Now, if we use first segment for passing variables and
procedure return addresses (normal stack usage), and second segment only for
local procedure variables, we will have the following advantages:
1) Local variables and return address will be physically (by means of CPU)
divided and it will not be possible to touch the return address by
overflowing local buffer.
2) The procedure introduces only one extra register -- GS, since EBP is
very often used for the stack frame.
Of course, this two segments can be made non-executable, just in case.

What we need to implement the idea: first, rewrite kernel to organise two
segments for every process and to place proper values into the segment
registers upon the program startup. Second, rewrite the compiler to support
the new scheme of local variables addresation. So, the changes are minimal,
in some sence.

As I said, I hardly need your criticism, suggestions, etc. of any type.
rea

Nicholas Weaver
13/08/03, 20:30
On Wed, Aug 13, 2003 at 02:28:33PM +0400, Eygene A. Ryabinkin composed:

> I have an idea on buffer overflow prevention. I doubt that it's
> new, but I haven't seen an implementation of it in any freely
> distributable Un*x system. So, I hardly need your comments on it.

Then why post this to a public list? Sorry, you are going to get
comments.

> Preliminary: I'm talking about Intel x86 architecture, but maybe it will be
> applicable to others as well.

> The idea itself: all (correct me if I'm wrong) buffer overflows are
> based on the fact that we're using the stack, referenced by SS:ESP
> pair, both for procedure return address and for local variables. It
> seems to me, that would we have two stacks -- one for real stack and
> one for variables -- it will solve a bunch of problems. So, my
> suggestion: let us organise two segments: one for normal stack,
> growing downwards, referenced by SS:ESP pair and the second one, for
> local variables, referenced by GS:EBP pair, with either upwards or
> downwards growing. Now, if we use first segment for passing
> variables and procedure return addresses (normal stack usage), and
> second segment only for local procedure variables, we will have the
> following advantages:

> 1) Local variables and return address will be physically (by means
> of CPU) divided and it will not be possible to touch the return
> address by overflowing local buffer.

> 2) The procedure introduces only one extra register -- GS, since EBP is
> very often used for the stack frame.

On the x86, introducing ONE extra reserved register into the calling
convention is a performance killer: the ISA is far too register poor
already.

> Of course, this two segments can be made non-executable, just in case.

> What we need to implement the idea: first, rewrite kernel to organise two
> segments for every process and to place proper values into the segment
> registers upon the program startup. Second, rewrite the compiler to support
> the new scheme of local variables addresation. So, the changes are minimal,
> in some sence.
>
> As I said, I hardly need your criticism, suggestions, etc. of any type.

This only stops attacks which overwrite the return address pointers on
the stack, it doesn't stop heap overflows or other control-flow
attacks.

As such, it seems like this only stops the same class of attacks which
StackGuard-style checking halts: manipulation of the return address
pointer through data overflows.

Since stackguard-techniques already exist, are very mature, and is
included as "On By Default" on OpenBSD and potentially on other
platforms (including possibly Windows, it is in Microsoft's latest
compiler and I think that all new code from Microsoft turns on this
compiler flag [1]), there is no sense inventing new structures, which
require substantially more changes to the infrastructure, which don't
offer any more protection.



[1] If microsoft doesn't have this flag turn on on their own products,
this would be a lawsuit waiting to happen.

--
Nicholas C. Weaver nweaver@cs.berkeley.edu

Crispin Cowan
13/08/03, 20:30
Eygene A. Ryabinkin wrote:

> I have an idea on buffer overflow prevention. I doubt that it's new, but I
>haven't seen an implementation of it in any freely distributable Un*x system.
>So, I hardly need your comments on it.
>...
> The idea itself: all (correct me if I'm wrong) buffer overflows are based on
>the fact that we're using the stack, referenced by SS:ESP pair, both for
>procedure return address and for local variables. It seems to me, that would we
>have two stacks -- one for real stack and one for variables -- it will solve
>a bunch of problems. So, my suggestion: let us organise two segments: one for
>normal stack, growing downwards, referenced by SS:ESP pair and the second one,
>for local variables, referenced by GS:EBP pair, with either upwards or
>downwards growing. Now, if we use first segment for passing variables and
>procedure return addresses (normal stack usage), and second segment only for
>local procedure variables, we will have the following advantages:
>
This is approximately what StackShield
<http://www.angelfire.com/sk/stackshield/info.html> does. However, it
does not appear to have been maintained since 2000.

Crispin

--
Crispin Cowan, Ph.D. http://immunix.com/~crispin/
Chief Scientist, Immunix http://immunix.com
http://www.immunix.com/shop/

Michal Zalewski
13/08/03, 22:15
On Wed, 13 Aug 2003, Eygene A. Ryabinkin wrote:

[ BUGTRAQ is probably not the best place for such a discussion, but
I'm not sure SECPROG is still alive and kicking, so... ]

> I have an idea on buffer overflow prevention.

Well, no, strictly speaking, you don't =) You have an idea for preventing
one of the symptoms, which is eip overwrite. Overwriting other stack
variables, pointers and flags in particular, or causing overflows
elsewhere in the memory would still be as lethal. But I'm too picky... to
the point.

The concept itself isn't particularly new, but I think that prior takes on
it considered splitting the call/ret and parameter stacks, instead of
local variables vs everything else. The latter is indeed a neat idea,
because it does not require major changes to the entire system at once -
the calling convention remains the same.

For a lesser performance impact, you should just keep arrays and alloca()
buffers in the secondary stack, I think. Since you need to modify the
compiler anyway, I think it's a wiser decision than putting everything on
the secondary stack. Besides, by keeping single integers (often used for
flags or to keep other vital parameters) and pointers away from the
buffers, you make it in some cases more difficult to take over the
application.

It has an advantage over non-executable stack patch, address randomization
or stack canaries in that it prevents ret address manipulation altogether,
making it impossible to return into PLT, program code, libc or whatnot, so
it's a nice idea.

--
------------------------- bash$ :(){ :|:&};: --
Michal Zalewski * [http://lcamtuf.coredump.cx]
Did you know that clones never use mirrors?
--------------------------- 2003-08-13 21:14 --

Jonathan A. Zdziarski
13/08/03, 23:15
I think this is overkill and will probably cause your applications to
run much slower than they already do. I don't see why one couldn't
simply put the variable information *after* the rest of the stack
information, instead of before, and have the kernel zero out the next
stack frame before it gets written to (although this may cause some
performance problems in itself). This would prevent a buffer overflow
from A) overwriting SS:ESP and B) overflowing code onto the next
stackframe.

Michal Zalewski
14/08/03, 00:30
On Wed, 13 Aug 2003 weigelt@metux.de wrote:

> Some languages offer runtime range checking, which should bring much
> security, but often is really slow :(

In the times of Java and XML used for almost everything, it's not like we
strive for every single CPU cycle nowadays in common applications and are
willing to sacrifice everything else for that. There are exceptions,
particularly in the multimedia domain, but codecs and drivers aren't a
common attack vector anyway, so we don't need range checking there that
badly. For other code, extra CMPL or so is really not that expensive.

The reason why most programmers don't use neat languages with strong
typing, range control, exception handling, pointers kept away from the
programmer, etc - unless they have to, of course - is quite different...
in fascist and easily readable languages, you feel being controlled, not
in control. We prefer to be in control to being instructed by an overly
picky machine, even if we evidently can't handle the situation in a
competent manner.

Plus, the point when code compiles and "just works" is further away,
because you need to resolve issues you wouldn't be aware of in C.

--
------------------------- bash$ :(){ :|:&};: --
Michal Zalewski * [http://lcamtuf.coredump.cx]
Did you know that clones never use mirrors?
--------------------------- 2003-08-13 23:06 --

weigelt@metux.de
14/08/03, 01:30
On Wed, Aug 13, 2003 at 12:13:27PM -0700, Nicholas Weaver wrote:

<snip>
> This only stops attacks which overwrite the return address pointers on
> the stack, it doesn't stop heap overflows or other control-flow
> attacks.
ACK. Often there are function pointers stored on the heap - so this
does not really help much.

It would be better to invest more time in fail-checking code, i.e.
in C: macros for memory operations which do range checks:

>>>>
static inline _sec_strcpy(char* dest, const char* src, int max)
{
int i;
if ((!dest)||(max<1)) return -1;
if ((max==0)||(!src)) return -1;
{
dest[0] = 0;
return 0;
}
i = strncpy(dest,src,max);
dest[max] = 0;
return i;
}

#define STRCPY_BUF(buffer,src) _sec_strcpy(&buffer, src, sizeof(buffer));
<<<<

Some languages offer runtime range checking, which should bring much security,
but often is really slow :(

<snip>
> [1] If microsoft doesn't have this flag turn on on their own products,
> this would be a lawsuit waiting to happen.
why (in detail) should they be attackable ? did they give any warranty ?

cu
--
---------------------------------------------------------------------
Enrico Weigelt == metux ITS
Webhosting ab 5 EUR/Monat. UUCP, rawIP und vieles mehr.

phone: +49 36207 519931 www: http://www.metux.de/
fax: +49 36207 519932 email: contact@metux.de
cellphone: +49 174 7066481
---------------------------------------------------------------------
Diese Mail wurde mit UUCP versandt. http://www.metux.de/uucp/

Craig Pratt
14/08/03, 01:30
On Wednesday, Aug 13, 2003, at 03:28 US/Pacific, Eygene A. Ryabinkin
wrote:
> Hi!
> I have an idea on buffer overflow prevention. I doubt that it's new,
> but I
> haven't seen an implementation of it in any freely distributable Un*x
> system.
> So, I hardly need your comments on it.
>
> Preliminary: I'm talking about Intel x86 architecture, but maybe it
> will be
> applicable to others as well.
>
> The idea itself: all (correct me if I'm wrong) buffer overflows are
> based on
> the fact that we're using the stack, referenced by SS:ESP pair, both
> for
> procedure return address and for local variables. It seems to me, that
> would we
> have two stacks -- one for real stack and one for variables -- it will
> solve
> a bunch of problems. So, my suggestion: let us organise two segments:
> one for
> normal stack, growing downwards, referenced by SS:ESP pair and the
> second one,
> for local variables, referenced by GS:EBP pair, with either upwards or
> downwards growing. Now, if we use first segment for passing variables
> and
> procedure return addresses (normal stack usage), and second segment
> only for
> local procedure variables, we will have the following advantages:
> 1) Local variables and return address will be physically (by means of
> CPU)
> divided and it will not be possible to touch the return address by
> overflowing local buffer.
> 2) The procedure introduces only one extra register -- GS, since EBP
> is
> very often used for the stack frame.
> Of course, this two segments can be made non-executable, just in case.

It's definitely good to be thinking of novel approaches to securing
code. Machines in the old days used to have memory partitioned in
similar fashions. But realize that overwriting return addresses via
stack trashing is only one way a program can be compromised.

C++ programs, for instance, have vtables, 'this' pointers, and all
sorts of other fun stuff that can be tinkered with - not to mention the
heap.

Approaches that involve partitioning executable code and data are
probably the easiest route. It becomes a major challenge to do
something interesting with return address manipulation when the memory
map is setup with execute-only in one range (for the code) and
non-executable (for stack & heap) in another. If you can't inject a
return address to the code of your choosing, than what's the point?

Dealing with dynamic code gets tricky (dynamic libs, Java, CLR, and
interpreters) in this environment. But it's quite do-able.

> What we need to implement the idea: first, rewrite kernel to organise
> two
> segments for every process and to place proper values into the segment
> registers upon the program startup. Second, rewrite the compiler to
> support
> the new scheme of local variables addresation. So, the changes are
> minimal,
> in some sence.
>
> As I said, I hardly need your criticism, suggestions, etc. of any
> type.
> rea

What fun is that? ;^)

Craig

--
Craig Pratt
Strongbox Network Services Inc.
mailto:craig .AT. strong-box.net


--
This message checked for dangerous content by MailScanner on StrongBox.

Patrick Dolan
14/08/03, 01:30
There is a flag for the Gnu C/C++ compilers, -fstack-protector, that will
implement ProPolice stack protection. It should prevent stack smashing
techniques.


On Wednesday 13 August 2003 05:28 am, Eygene A. Ryabinkin wrote:
> Hi!
> I have an idea on buffer overflow prevention. I doubt that it's new, but I
> haven't seen an implementation of it in any freely distributable Un*x
> system. So, I hardly need your comments on it.
>
> Preliminary: I'm talking about Intel x86 architecture, but maybe it will
> be applicable to others as well.
>
> The idea itself: all (correct me if I'm wrong) buffer overflows are based
> on the fact that we're using the stack, referenced by SS:ESP pair, both for
> procedure return address and for local variables. It seems to me, that
> would we have two stacks -- one for real stack and one for variables -- it
> will solve a bunch of problems. So, my suggestion: let us organise two
> segments: one for normal stack, growing downwards, referenced by SS:ESP
> pair and the second one, for local variables, referenced by GS:EBP pair,
> with either upwards or downwards growing. Now, if we use first segment for
> passing variables and procedure return addresses (normal stack usage), and
> second segment only for local procedure variables, we will have the
> following advantages:
> 1) Local variables and return address will be physically (by means of CPU)
> divided and it will not be possible to touch the return address by
> overflowing local buffer.
> 2) The procedure introduces only one extra register -- GS, since EBP is
> very often used for the stack frame.
> Of course, this two segments can be made non-executable, just in case.
>
> What we need to implement the idea: first, rewrite kernel to organise two
> segments for every process and to place proper values into the segment
> registers upon the program startup. Second, rewrite the compiler to support
> the new scheme of local variables addresation. So, the changes are minimal,
> in some sence.
>
> As I said, I hardly need your criticism, suggestions, etc. of any type.
> rea

--
Patrick Dolan
UNT Information Security

PGP ID: E5571154
Primary key fingerprint: 5681 25E4 6BE6 298E 9CF0 6F8D B13B 2456 E557 1154

Jingmin
14/08/03, 02:00
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

This is not a complete solution. It's just like non-executable
stack that prevents a certain number of buffer overflows in the
stack. Heap overflows, or some advanced buffer overflow attacks
can easily bypass this approach. Furthermore, let's suppose we
have the following code. How do you prevent the overwrite of
the critical variable 'pass' if 'buf' is overflowed (yes, it's
an extreme case, but still possible in some programs)?

foo (char* password)
{
int pass = 0;
char buf[N];

while (*password)
*buf++ = *password++;

...
if (pass)
grant_privilege (...);
else
error ("invalid password");
}

On Wed, 13 Aug 2003, Eygene A. Ryabinkin wrote:

> Hi!
> I have an idea on buffer overflow prevention. I doubt that it's new, but I
> haven't seen an implementation of it in any freely distributable Un*x system.
> So, I hardly need your comments on it.
>
> Preliminary: I'm talking about Intel x86 architecture, but maybe it will be
> applicable to others as well.
>
> The idea itself: all (correct me if I'm wrong) buffer overflows are based on
> the fact that we're using the stack, referenced by SS:ESP pair, both for
> procedure return address and for local variables. It seems to me, that would we
> have two stacks -- one for real stack and one for variables -- it will solve
> a bunch of problems. So, my suggestion: let us organise two segments: one for
> normal stack, growing downwards, referenced by SS:ESP pair and the second one,
> for local variables, referenced by GS:EBP pair, with either upwards or
> downwards growing. Now, if we use first segment for passing variables and
> procedure return addresses (normal stack usage), and second segment only for
> local procedure variables, we will have the following advantages:
> 1) Local variables and return address will be physically (by means of CPU)
> divided and it will not be possible to touch the return address by
> overflowing local buffer.
> 2) The procedure introduces only one extra register -- GS, since EBP is
> very often used for the stack frame.
> Of course, this two segments can be made non-executable, just in case.
>
> What we need to implement the idea: first, rewrite kernel to organise two
> segments for every process and to place proper values into the segment
> registers upon the program startup. Second, rewrite the compiler to support
> the new scheme of local variables addresation. So, the changes are minimal,
> in some sence.
>
> As I said, I hardly need your criticism, suggestions, etc. of any type.
> rea
>

__________________________________________________ __________
Jingmin (Jimmy) Zhou Mail : jimmy AT mtc.dhs.org
Web : www.mtc.dhs.org ICQ : 19587415

The future is not set. There is no fate but what we make
for ourselves. - Terminator II, Judgement Day
__________________________________________________ __________
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2 (GNU/Linux)

iD8DBQE/OoDcVieKrU8JxnkRAn8bAKDWdQCH5VRXnEFYHXYsEgE/c3kYcQCdGPRZ
yuxDDOlflab+yfkGUAQn1nM=
=SkN8
-----END PGP SIGNATURE-----