Re: Buffer overflow prevention

From: Andreas Beck (becka@uni-duesseldorf.de)
Date: Wed Aug 13 2003 - 16:03:17 PDT

  • Next message: Helmut Hauser: "Re: Analysis/decompilation of main() of the msblast worm"

    "Jonathan A. Zdziarski" <jonathanat_private> wrote:
    > I think this is overkill and will probably cause your applications to
    > run much slower than they already do.
    
    Ack. Sacrificing a register is probably pretty bad for x86.
    
    I see more problems, though. The usual memory layout is to have 
    code and fixed size data somewhere at the start of the virtual address
    space, then above that the heap and the stack at the end.
    
    This allows to let the heap top grow towards the stack bottom, allowing 
    for maximum usage of the virtual address space which is small enough 
    on x86 anyway (4GB isn't much for some apps ...)
    
    Having another thing to place leaves the question of where to place
    that without getting in the way for both common cases of apps with high
    stack usage or high heap usage. 
    
    And please do not argue for using a segment of its own. 
    That causes really evil headaches when handling pointers.
    
    
    > I don't see why one couldn't simply put the variable information 
    > *after* the rest of the stack information, instead of before, 
    
    Because that is not how C (and other languages that use its calling
    convetion) works. The point is, that you can open subscopes anywhere
    like this:
    
    int some_function(int some_parm) {
    	int some_var;
    	...
    	{
    		int some_local_var;
    	}
    }
    
    Using the usual layout that looks a bit like this:
    
    some_parm
    ret-address
    some-locally-saved-regs
    some_var
    some_local_var
    
    while inside the inner brackets, and the same just without the last line
    while outside.
    
    This is easy to implement and partially even supported by the CPU design. 
    It is hard however to keep the ret address below anything else, as you 
    would have to continuously copy it around.
    
    AFAIK one could configure the stack to walk up instead of down, but that 
    would pose a similar problem WRT the traditional memory layout and 
    stuff like the traditional brk()/sbrk().
    
    
    Moreover it just helps in a few specific cases, as do many other
    approaches.
    
    An increasingly popular problem are format string and stack smashing
    attacks. 
    
    Especially format string attacks are often capable to write to any
    byte in memory. This way any function pointer that is called later
    is at risk - no matter what you do to protect yourself using memory
    separation techniques.
    
    Of course the problem might be mitigated severely, as you cannot 
    just download the shellcode, if all the writeable areas are not
    executable anymore, but that just moves the challenge to find 
    some clever way to abuse functions within the original code.
    
    I have no doubt people will find a way - unicode exploits exist
    as do exploits that can only use a specific subset of characters.
    
    
    > and have the kernel zero out the next stack frame before it gets written 
    > to (although this may cause some performance problems in itself).
    
    Highly recursive programs might not be amused. Try running a program
    that extensively uses malloc with efence for a taste of what happens.
    
    A simple thing like a function call should very probably not cause the
    System to fiddle with MMU tables ... hmm - or what did you mean with
    zeroing out?
    
    
    > This would prevent a buffer overflow from A) overwriting SS:ESP and 
    > B) overflowing code onto the next stackframe.
    
    Basically a stack canary should do the same.
    
    
    So I agree with some others here: Fix the problem, not the symptom.
    
    And the problem is, that buffers are overflowed, because the APIs
    for string handling are ... umm ... let's say ... suboptimal.
    
    A common length-checking set of functions along the lines of the
    C-library str* and sprintf functions plus a few others would already 
    help a lot. Not that these do not exist ... the problem is to
    teach people to exclusively use them.
    
    
    In a similar way, automatic overflow checking (e.g. selectable by a
    compiler pragma so we don't loose performance, when it is not critical 
    anyway) would prevent quite some of the integer-overflow problems we 
    are seeing.
    
    
    CU, Andy, waiting for another rout of out-of-office junk.
    
    -- 
    = Andreas Beck  |  Email :  <becka@uni-duesseldorf.de> =
    



    This archive was generated by hypermail 2b30 : Thu Aug 14 2003 - 11:31:07 PDT