Slashdot Mirror


OpenBSD Stomping On Buffer Overflows

A reader writes:"There's a story on ZDNet that describes how Theo de Raadt & co. are hoping to eliminate buffer-overrun exploits for good. On closer inspection, it's a scheme to stop a buffer-overrun leading to executable code. It doesn't stop the buffer-overrun itself."

10 of 47 comments (clear)

  1. What about by Anonymous Coward · · Score: 4, Interesting

    What about stackguard? Why isn't it in use everywhere? Or libsafe for that matter? Or Openwall Project kernel patch for Linux? Can anyone please tell me why no one uses it?

  2. It's a shame that segments work the way they do. by wowbagger · · Score: 4, Interesting

    I'm about to say something I would have never dreamed of saying a couple of years ago.

    It is a shame that Intel made segments work the way they do, because a minor tweak and segments would have been the perfect way to prevent buffer overflows in hardware.

    Consider: what if, instead of segment descriptors having to live in the GDT and LDT, they could be loaded into a register from a normal memory limit? True, they would then have been useless for OS level protection, but I assert that is what the page map is for (yes, the page mapper didn't exist prior to the 386, and enhanced segments showed up in the 286).

    In some of the DSPs that I work with, you have registers to specify a region of memory. When you access off these registers, memory bounds are enforced by the chip (this allows for circular regions of memory, bit reversed addressing, and other weird things you need when doing DSP work).

    What if you could have done something like this:

    buffer:
    ds 1024 ; a buffer of stuff
    buffer_descr:
    dd buffer ; where buffer starts
    dd 1024 ; sizeof(buffer) ...
    LDPROT ES,buffer_descr ; set limits checking
    LD ES:(ESI),EAX ; store to buffer with checking.


    Thus, any access out of bounds would throw a SIGSEGV.

    Then, the code could have provided protection against overflows without explicit checking on every array access. True, this would not protect you if all you were given was the buffer address, but in the presense of this sort of hardware, GCC could have been modified to make a char (*a)[] (pointer to array of char) be three elements - base, sizeof(), index.

  3. Great ideas! But what about the "file system"? by Hanashi · · Score: 2, Interesting
    These security measures sound pretty good, though I think that the memory layout changes and the segmentation might cause some problems until developers get used to the new model. It's probably worth it, though, in the long run. The extra layers seem like good precautions to take, especially the randomized layout. Most of the canned exploits these days come with scads of pre-computed parameters for all the different OSes. Randomized memory layouts will make this sort of thing much more difficult, though I'm sure the exploit writers will eventually learn to compensate.

    But my real question is about that ZDNet article. It said that the segmentation measures came from "hacking the BSD file system". It sounds to me like the reporter got confused. Can anyone familiar with the code comment on this?

    --
    Check out my eclectic infosec blog at InfoSecPotpou
  4. Is the randomization per machine, per build, etc? by blinka · · Score: 3, Interesting
    It's hard to be sure from the article itself (given that reporters are unlikely to understand the distinction, much less care), but in what way is this stack manipulation random? Quoting the article:

    The group randomised where in memory the "stack" -- a structure that holds applications and their data -- resides, so that code designed to exploit buffer overflows will have to be tailored to the system's memory layout.

    but this is still vague. It could be:

    • Per kernel. (All processes on this box have the same stack layout)
    • Per process. (All processes have a different stack layout, though threads and forked versions may have the same.)
    • Per binary. (Each program's stack layout is determined at compile time and will not change on invocation.)
    • Completely random (Each program is different every time, or better yet each function call is different each time.)
    This would have a very import impact on how well a hacker could brute force your processes. If a given process always has the same stack layout then you can eventually brute force it, just like we currently can brute force offsets. The bar is higher of course - only one variable is being manipulated - and a hacker will probably go to easier machines before too long. But a truly randomized per process invocation or better yet per function call invocation would be such a moving target that it should be extreemly unlikely to succeed. I'm really excited.
  5. buffer over flow exploits not truly eliminated by mzs · · Score: 3, Interesting

    It is clear from gems like this from the article that the reporter was confused so that it is impossible from the article itself to really undrestand how the three approaches would work:

    "An overflow exploit generally works when an attacker sends a program requesting too much information. The data usually includes two components: one that crashes the application and one that's either a program or a memory address that points to a program that the attacker would like to run. When the application crashes due to the first component, the operating system will execute the second.

    It is important to point out that this looks to be yet another in the attempts at making buffer over flow exploits difficult yet not quite impossible. Once you put return addresses on the stack (the same stack with an over flow) an exploit can jump to any place it likes in text even without an executable stack.

    The details about the tag around import addresses and the random stack offsets could make exploits _very_ difficult. If the base of the stack is random from one invocation of the program to the next that does not give much more once you cannot execute on the stack so it is probably more than that. Also if the tag around important addresses (say the address of the buffer to system(3C)) incorpates randomness from one invocation of the program to the next then it would be hard to over flow a buffer and exploit it.

    In any case even if you had a sytem where a buffer over flow could not be exploited to run arbitrary code but almost any random junk will still crash the broken prorgam, that does not do much to prevent DoS now does it.

  6. No, it is not the kernel devs fault. by wowbagger · · Score: 2, Interesting

    If I can overwrite a buffer, I can screw you machine. I don't care if the stack is not executable - I can modify the return address and fake a call to exec with "/bin/bash" as the args.

    If auto storage is removed from the call/return stack, I can overwrite another variable somewhere else, perhaps tricking your program into thinking it is debug mode. It gets harder, but not impossible.

    What I was discussing was fine-grained memory bounds checking - which you simply cannot do when all you have is 8192 LDT and 8192 GDT entries, some of which have to be IDT gates, system call gates, task state segments, etc.

  7. Overlapped code/data a mistake. BOUND instr. avail by AHumbleOpinion · · Score: 2, Interesting

    I understand your point, I understand it's usage, it does not change the fact that kernel developers made a mistake with overlapped code and data. "It gets harder, but not impossible" is a weak rationalization. Furthermore Intel's BOUND instruction does much of what you suggest. It verifies an address is in a particular range and throws an exception if it is out of range.

  8. Re:Overlapped code/data a mistake. BOUND instr. av by wowbagger · · Score: 2, Interesting

    My statement about "it gets hard" was for a seperate hardware stack and auto data storage, not seperate code and data spaces.

    Even if the program space and the data space were completely seperate I can trivially smash the stack and get a shell - again, all I have to do is overwrite the current function return address with the address of execv, and the preceeding addresses with a bogus return address and pointer to the strings "/bin/sh", 0, and 0. No code segment overlap needed.

    Secondly, the BOUND instruction would have to occur before EVERY potential operation, bloating code size (bu-bye cache) and slowing the machine down.

    Your assertion that seperate code and data spaces in the kernel is like locking the doors on a convertable - it may make you feel good, but it does not really do anything.

    Lastly, most stack smashes happen in user space, not kernel space. If you wish to critizize anybody, criticize the developers of GCC and libc.

  9. Re:Overlapped code/data a mistake. BOUND instr. av by AHumbleOpinion · · Score: 2, Interesting

    Secondly, the BOUND instruction would have to occur before EVERY potential operation, bloating code size (bu-bye cache) and slowing the machine down

    No, it is not needed globally. It can be selectively used just like the code you suggested previously. Secondly, your solution bloats the code as well, your LDPROT instruction and segment override prefix consumes cache space as well.

    Your assertion that seperate code and data spaces in the kernel is like locking the doors on a convertable - it may make you feel good, but it does not really do anything

    Lastly, most stack smashes happen in user space, not kernel space ...

    You misunderstand. I would have preferred code and data segments that did not overlap. That applies to both the kernel and user mode.

    What this would do is prevent an entire class of attack where arbitrary code is introduced by an overrun and then executed.

    ... If you wish to critizize anybody, criticize the developers of GCC and libc

    Not for code/data overlapping, the kernel developers are the ones who decided the segmentation model. Possibly for the lack of an option to insert BOUNDS instructions, but gcc may actually have a "check array bounds" option.

  10. Re:Overlapped code/data a mistake. BOUND instr. av by wowbagger · · Score: 2, Interesting

    Actually, my solution does not bloat the code as much - you would incur the penalty of loading the protection once per scope - after that all subsiquent accesses via that register are "free". With the BOUND instruction, you have to insert it before every access, unless you can somehow insure that the check is unneeded (e.g. a loop index where you can BOUND the start and the end value of the loop var).

    As for the code/data space issue - the issue at hand is stack-smashing and array bounds violations - neither of which is measurably affected by seperation of code and data space.

    As for "who's to blame" for the current code/data overlap - it's a bit of a toss-up: GCC has some of the blame, LIBC some of the blame, the kernel some of the blame. But having done development in an environment where the code and data segments did not overlap I can tell you that you get bit by all SORTS of problems - const char *'s that the compiler wants to put in the code segment, pointer conversions from void * to void (*)(), and so on.

    It ain't as easy as it sounds, trust me.