Which approach should be taken for audit subsystem
Robert Watson
rwatson at FreeBSD.org
Fri Apr 11 17:36:18 GMT 2003
On Fri, 11 Apr 2003, Pawel Jakub Dawidek wrote:
> On Wed, Apr 09, 2003 at 10:26:07PM -0400, Robert Watson wrote:
> +> [...] One of the problems that is
> +> frequently stumbled into is that of race conditions on arguments. Almost
> +> all (although not quite all) such modules have problems wherein the
> +> arguments are copied into the kernel twice: once by the wrapper, and once
> +> by the original kernel service code. With an SMP box, or even with a
> +> single-threaded box if you force page faults, you can race to replace an
> +> argument between the check and use, resulting in the wrapper using one
> +> version, and the kernel using another. Neils Provos modified systrace to
> +> add a system call argument look-aside buffer when I pointed this
> +> vulnerability out to him. I talk a bit more about this vulnerability and
> +> some related vulnerabilities in my DISCEX III paper...
>
> And how to handle with it?
>
> My idea is:
>
> USERLAND -> WRAPPER -> KERNEL
>
> When wrapper is running it copies arguments to kernel memory. Now it
> could safety operate on arguments and before real syscall is called
> arguments are copied to userland to old place or memory is allocated in
> userland and arguments are copied to new place. Is this is safe enough?
>
> But there could be a problem with copying arguments to new place,
> because if we are using obreak() (this is the simplest and cleanest way)
> there could be some limits on memory size that could be allocated by
> process.
>
> What is your opinion about this?
Well, I believe it's still raceable. Consider the following scenario:
A process creates two threads:
Thread 1 invokes a system call (perhaps rename).
The wrapper copies in the string argument in Thread 1.
<This is where I'd normally race your wrapper if you weren't copying
out>
The wrapper copies back out the string argument in Thread 1.
Thread 2 writes over the string argument, replacing the pathname such
that the kernel will see the new string rather than the one the wrapper
saw.
The kernel copies in the string argument and acts on it.
Alternatively, it could be two processes using System V shared memory, an
mmap'd file, or whatever.
When I talked with Neils about this vulnerability, he suggested a few
possible workarounds:
(1) Write out the string to the stack gap in process memory, and randomize
the location. This can be raced, but it involved more work.
(2) Use VM tricks to prevent the page holding the source from being
modified. This would prevent the attack, but it might also break
other things, including if the system call was intended to write back
out to the same memory, or adjacent memory, or if other threads assume
they can modify that memory.
(3) Insert a look-aside buffer to cause the wrapper and kernel proper to
use the same contents. This works, but at the cost of modifying your
primitives and having to cache potentially large amounts of data for
the duration of the system call. It might also cause odd behavior for
AIO interfaces depending on how you implement writes to the cache
(needs to be write-through).
In the end, I believe he selected (3) since the other two options had
fairly serious flaws/sticking points.
Having done several wrapper-derived security modules, one of the big
design requirements for the MAC Framework was that it avoid that sort of
race, as well as be more locking friendly for kernel locking. I talk
about this a fair amount in the DISCEX paper, and a bit in the FREENIX
2003 paper.
Robert N M Watson FreeBSD Core Team, TrustedBSD Projects
robert at fledge.watson.org Network Associates Laboratories
To Unsubscribe: send mail to majordomo at trustedbsd.org
with "unsubscribe trustedbsd-audit" in the body of the message
More information about the trustedbsd-audit
mailing list