[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Exec-Shield vs. PaX



since a few points have been made regarding $subject, let me clear
up a few of them:

1. 'It seems that exec-shield does 99% of what PaX does'

  i don't know the origin of that number above, for now i'll just
  stick to the facts i know:

  - PaX implements perfect non-executable pages on amd64, i386,
    ia64, parisc, ppc, sparc and sparc64 whereas Exec-Shield has
    some imitation of it only on i386 (it's not true per-page).

  - PaX implements a concept about how runtime code generation
    should be done, there's nothing similar in Exec-Shield, and
    it seems that Ingo does not even understand why this is
    important (for Ingo: please read and understand [1] before
    you call something bogus, see below for more).

  - PaX implements best-effort randomization of the entire
    address space, Exec-Shield does it too but at a higher
    code complexity and a lower entropy rate while having
    a worse effect on the kernel entropy pool.

2. paxtest 'proofs'

  i saw several people point to paxtest results to 'prove' how
  good Exec-Shield it is. it is not. first, Exec-Shield has a
  fundamental design problem stemming from the lack of understanding
  or design on Ingo's part (what i call MPROTECT in PaX). you'll
  really have to read the PaX design docs to understand its role
  in the grand scheme of things (see below for a bit more).

  second, paxtest had some bugs which Exec-Shield exposed and made
  Exec-Shield appear better than it is. i've fixed them here and
  expect to release 0.9.5 today or so. the results now look like:

PaXtest - Copyright(c) 2003 by Peter Busser <peter@adamantix.org>
Released under the GNU Public Licence version 2 or later

It may take a while for the tests to complete
Test results:
PaXtest - Copyright(c) 2003 by Peter Busser <peter@adamantix.org>
Released under the GNU Public Licence version 2 or later

Executable anonymous mapping             : Vulnerable
Executable bss                           : Vulnerable
Executable data                          : Vulnerable
Executable heap                          : Vulnerable
Executable stack                         : Vulnerable

  the above changes are the result of Ingo's approach to create
  non-executable memory on i386, they're not per page as a simple
  mprotect on the top of the stack shows. before i get accused of
  specifically rigging the tests, i'll tell you that running
  multithreaded apps would have almost the same effect (only the
  main stack would stay non-exec under Exec-Shield). needless to
  say, PaX passes all the above as before.

Executable anonymous mapping (mprotect)  : Vulnerable
Executable bss (mprotect)                : Vulnerable
Executable data (mprotect)               : Vulnerable
Executable heap (mprotect)               : Vulnerable
Executable shared library bss (mprotect) : Vulnerable
Executable shared library data (mprotect): Vulnerable
Executable stack (mprotect)              : Vulnerable
Anonymous mapping randomisation test     : 8 bits (guessed)
Heap randomisation test (ET_EXEC)        : 13 bits (guessed)
Heap randomisation test (ET_DYN)         : 13 bits (guessed)
Main executable randomisation (ET_EXEC)  : No randomisation
Main executable randomisation (ET_DYN)   : 12 bits (guessed)
Shared library randomisation test        : 12 bits (guessed)
Stack randomisation test (SEGMEXEC)      : 17 bits (guessed)
Stack randomisation test (PAGEEXEC)      : 17 bits (guessed)
Return to function (strcpy)              : Vulnerable
Return to function (strcpy, RANDEXEC)    : Vulnerable
Return to function (memcpy)              : Vulnerable
Return to function (memcpy, RANDEXEC)    : Vulnerable

Executable shared library bss            : Vulnerable
Executable shared library data           : Vulnerable

  these two had bugs in them (they were trying to execute code
  from the wrong location). i find it somewhat funny that the
  Exec-Shield proponents (including Ingo himself) have used the
  previous false results as a justification for their claims.

  apparently none of you understood what the tests and Exec-
  Shield did, otherwise you would have known that Exec-Shield
  cannot possibly pass these tests due to its design (or at
  least not without going down the OpenBSD road).

Writable text segments                   : Vulnerable


3. MPROTECT is bogus

  it is not. Ingo says so because he did not understand how
  PaX works. the short story (there's no substitute to reading
  the docs!) is that in PaX we want to handle the problems posed
  by memory corruption bugs. all such bugs. the approach chosen
  in PaX is based on preventing exploit techniques from working
  (vs. preventing specific bug situations from occuring, at least
  for now, efficient full runtime bounds checking will have to
  wait a bit). in the docs you will find a classificaiton of
  exploit techniques:

  (1) introduce/execute arbitrary code
  (2) execute existing code out of original program order
  (3) execute existing code in original program order with arbitrary data

  every possible exploit technique against memory corruption bugs
  belongs to one of the above (note that it is a classification of
  exploit techniques, not bugs, that is, a given technique can be
  used against different bugs and a given bug can be exploited by
  more than one technique). the idea in PaX is that we try to prevent
  entire classes of exploit techniques from working, starting with
  the easy ones and going up to the hard problems. in the present
  situation (1) has been dealt with, (2) is in the works, and (3)
  is somewhere in the future (pending some research).

  dispite Ingo's claim, restricting page protection transitions on
  memory pages (which is what the mprotect() restrictions do) is
  mandatory for our purposes, far from being bogus. without it, it
  remains possible to introduce/execute new code into a process.

  with MPROTECT and the ACL system (or a read-only chroot) to prevent
  arbitrary file mappings and/or creation you can prevent attack
  method (1), guaranteed. the fact that PaX does not contain the
  ACL part is the simple consequence of its being used by different
  projects who want to do it their way, i explicitly chose not to
  stand in there (for the same reason PaX itself does not prevent
  information leaking in /proc, that's up to the patch integrators).

  Ingo also implied that executing an in-memory shell script via
  system() does (1). it does not, it does (2). think about it,
  (1) is about executable machine code, a shell script is not
  machine code.

  Ingo also confuses MPROTECT with mprotect() restrictions, the
  former is much more than the latter, it's about separating the
  writable pages from executable ones for the purposes of preventing
  (1), something that Exec-Shield doesn't (cannot) do.

  Ingo also argues about non-completness... in the main doc there's
  an explicit paragraph for people like him, please read it again.
  it will explain to you why you cannot judge a building block on
  its own, only together with others. the combined effect is much
  more than what the individual pieces are capable of. PaX is not
  a finished project, there are several pieces of the puzzle yet
  to be written.


4. PaX breaks apps, specs, whatnot

  i chose to enable most of PaX by default (while allowing as many
  apps to run as possible, e.g., that's why ELF text relocs are
  allowed by default). since PaX explicitly wants to prevent runtime
  code generation, obviously apps that want to do it will no longer
  work. there are two categories of such apps and two ways to fix
  them.

  - apps that don't really need to generate code runtime but still
    do it for whatever reason: they are buggy and must be fixed at
    the source level. one prime example is the module loader code
    in XFree86 which assumes that malloc()'ed memory was executable
    whereas it was not (run strace on any app and you'll see how
    glibc uses mmap() to request memory). another good example is
    the various .plt stubs that are runtime generated on many risc
    archs, for now i added emulation to them, but eventually they'd
    better be redesigned a la i386.

  - apps that by their nature want to generate code runtime (e.g.,
    java). they're  broken for good which many people noticed by now.
    that's good, that was my purpose because i wanted to draw
    attention to the fact that runtime code generation is an
    important privilege that should be carefully managed (as it
    happens to be also one of the exploit techniques). of course if
    you don't agree with my exploit technique classification and
    the general solution approach i took with PaX, then there's
    nothing to discuss and you can stop reading this ;-). for those
    who are interested in solving this problem, please read my
    proposal in mprotect.txt (available at [1]).

  about PaX breaking specs: i urge Ingo and others saying this to
  point me to the precise location in SUSv3 or POSIX 1003.1-2001
  that PaX conflicts with. for all i know, none of these standards
  give ANY guarantees about the ability to generate code at runtime.
  and rightly so, just think of non-coherent cache systems, you
  need explicit userland (or kernel assisted, not sure how all such
  systems solve it) flush interfaces, nothing like that is in the
  standards (msync() is for something else, nor is it used by any
  code i know that generates code at runtime, so they'd be non-compliant
  even if msync() was meant for this purpose).

  about SEGMEXEC and the 1.5GB limit: yes, it's true, yes, it could
  be changed (if one can live with a more limited range for executable
  mappings), yes, it would need some userland tagging, yes, i have
  ideas for it, no, it's got nothing to do with PT_GNU_STACK. the
  longer story is that the majority of users don't need to care about
  this address space limit, it's mainly databases and some scientific
  applications that run into. mind you, they already had this problem
  before when people were using 1-2 GB userlands (which back then
  was the way to allow the kernel to use more than 1 GB RAM, and even
  these days it's the only way to go if you don't want HIGHMEM).

  as for userland tagging: i'd like to have much more control over
  what userland can request from the kernel than what PT_GNU_STACK
  allows. for one, requesting executable stacks is irrelevant, it
  should be about requesting permission to generate code at runtime
  (or just use my proposed mmap() interface changes), request
  randomization, etc. i'd also like to have control over the
  placement of the stack and the userland address space size in
  the future. for now chpax is the quick & dirty solution for
  tagging, although users of complete systems with ACLs (grsec
  or RSBAC in Adamantix) can control the PaX flags via ACLs,
  no need for binary modifications. mind you, it's somewhat
  misleading to state that PT_GNU_STACK is a better solution
  than chpax, after all, someone HAS to decide whether a given
  module needs it or not (before someone points it out, trampolines
  are only a small and least important part of the problem), the
  toolchain cannot automatically determine it, so it needs the
  exact same manual work as chpax.


i'm sure i left questions unanswered, feel free to ask here or
in private.

[1] http://pageexec.virtualave.net/docs/

PS: please CC on responses.



Reply to: