Bugtraq mailing list archives

PAX & the Future of buffer overflows ?


From: Crispin Cowan <crispin () WIREX COM>
Date: Tue, 31 Oct 2000 00:58:06 -0800

Thomas Dullien wrote:

Well, I assume you all have read the PaX paper at pageexec.virtualave.net.

Yep :-)


So it is possible to have readable, non-executable memory pages, at a
not too bad performance hit of up to 10%. This is very cool. The
traditional
way of exploiting buffer overruns and format string vulnerabilities are
pretty much non-functional if the OS kernel can ensure no writable page
can be executed.

This part confuses me :-)

Thomas' analysis below reminds us that making data pages non-executable
leaves open the return-into-libc style of attacks.  We predicted this kind of
work-around for segment access controls in our paper early this year
dissecting the buffer overflow problem space
http://immunix.org/StackGuard/discex00.pdf  The paper classifies buffer
overflow attacks in two dimensions:


   * How the attack code is placed in the victim program's address space.
     There are two options:
        o Inject it:  i.e. the attack code is written into data space.
          PAXstops this
        o It is already there:  i.e. use a variation on return-into-libc.
   * Making the program jump to the attack code:  This is generally done by
     changing a "code pointer" to point to the attack code, and waiting for
     the program to dereference the code pointer.  The choice is which code
     pointer to change, which in turn depends on what the buffer is adjacent
     to:
        o Activation Records: i.e. function call return addresses, the
          classic "stack smash" from Aleph's cookbook.
        o Function Pointers:  i.e. "void (* foo)()" declares the variable foo
          to be a pointer to a function returning void.  The SuperProbe
          exploit of long ago, and many since, have used buffer overflows to
          change function pointers that happened to be adjacent to
          overflowable buffers.
        o Longjmp Buffers:  When you do a setjmp(buffer), it stores your
          state in the buffer structure, including your program counter
          (PC).  Subsequent longjmp(buffer) calls will jump you to that PC
          value.  If buffer is adjacent to an overflowable buffer, then the
          attacker can change the PC value to point to the attack code.

Memory segment access controls are fundamentally a script kiddie defense.
Any attack that can be defeated by PAX, can also be re-worked to not depend
on injecting the shell code.  Therefore, by any classical definition of
"security", it fails to actually provide defense, making it an obscurity
defense that merely makes it more difficult to write exploits. Openwall
http://www.openwall.com/linux/ provides a similar benefit, but without the
10% performance overhead.

It is a really cool hack that you can implement non-executable data pages on
the x86 ISA that does not support read/no-exec pages by messing with the
TLB caches.  But I don't really see the security value.

I am *not* saying that PAX has no value.  However, that value is restricted
to debugging.  Clearly executing data pages in the common case is an error,
and when testing a program, you want to know about it.  However, as Saltzer &
Schroeder tell us in their seminal paper
http://web.mit.edu/Saltzer/www/publications/protection/index.html obscurity
does not equal access control, because the intelligent attacker will simply
adapt to the obscure requirements.


Does this mean buffer overflows and format string vulnerabilities are dead?

On systems that actually implement this kind of protection, they are
going to have a lot less importance than before. Cutting & pasting
shellcode
just won't do it any more.

So what can you still do with a buffer overrun ?
This depends largely on the particular situation and has to be decided on
a case-by-case basis, but generally a stack overflow can still be used
to issue one libc/msvcrt call with arbitrary arguments as long as they
do not include a NULL byte as args. Now with a single system() or exec()
you can do a few funky things, and even memcpy() is a very cool thing
to execute. If you manage to keep the stack in a consistent state and
you don't crash the application, you might even be able to do this several
times in a row.
Format string vulnerabilities still allow you to write to any location in
memory. Patching internal variables and overwriting vtable pointers is
a funny thing to do.

So to wrap it up: The PaX project is IMO awesome. But even if buffer over-
runs and format string vulnerabilities loose the instant remote root shell
danger, they can still allow a skilled attacker to do things he's not
supposed to. And its only up to the creativity of the attacker what he
does. So it raises the bar for us all :) but just might make writing
exploits an interesting business again.

Now that's the part of the analysis I can agree with. :-)

Crispin

--
Crispin Cowan, Ph.D.
Chief Research Scientist, WireX Communications, Inc. http://wirex.com
Free Hardened Linux Distribution:                    http://immunix.org


Current thread: