OpenBSD Journal

Theo de Raadt on pinsyscall(2)

Contributed by rueda on from the pinhead dept.

Theo de Raadt (deraadt@) posted to tech@ a message entitled pinsyscall, execve, and rop pivots, etc. It explains pinsyscall(2), OpenBSD's latest security innovation.

We reproduce the posting below with added links:

These days, when attackers find bugs they cannot simply load code onto
the stack or a databuffer and run it there.  Those days are over because
an increasing number of restrictions were imposed upon address space
use.

So they tend to use ROP.  This is done by loading return addresses onto
the stack, which point at small chunks of prexisting code (called
gadgets), which then operate on the registers, top of stack, and other
conditions known at the moment of attack, and influence the program state
and gain escalation using pre-calulated means.

The first challenge for a ROP programmer is know where the gadgets, they
must be in a text segment.  That is usually done by getting an info leak
from the same process before the ROP upload -- basically, the address of
some code section must be known (to de-ASLR it).

Of course you also know the layout, so random relinking is a pain, because
knowing one address in libc no longer tells you where the rest of libc's
gadget base is.

The same ASLR + random-relinking applies to the other executable chunk in
the address space: ld.so

We are also starting to randomly-relink other security sensitive programs
(sshd first)

Anyways, this ROP attack method can take a few approaches, and these are
probably the most common:

1 - modify some "global state", repair the damage, return to normal operation
    and depend upon that changed global changed to provide the goal of giving
    access or escalation.  I think this is complex, domain-specific work,
    and very rare

2 - return-to-libc, this means to use a partially uploaded + further modified
    block of arguments, and jump to libc execve or system.  The random
    relinking really gets in the way here.

3 - another approach is to create the arguments as in #2, but to create a
    system call entry by loading the system call number and jumping straight
    to a system-call instruction.  This can be easier than #2, because you
    don't need to find system or execve or family, but can find *any* system
    call stub that works.  It might not be in libc, it might be in ld.so (which
    is also ASLR located, and also random-relinked).  The syscall stub cannot
    be in a dynamic main program's text segment because that blocks system
    call entry (msyscall).  The alure of method #3 is that ld.so contains
    30 system call entry points, and libc.so contains 150+, so if you have
    enough info you can jump to any of your choice.

Well, you can't do #3 as easily anymore.  I have introduced
pinsyscall(2), which lets ld.so [dynamic programs] or crt0 [static
programs] tell the kernel where the SPECIFIC execve entry point is, and
any other entry point is invalid and kills the program.

Now the attacker must precisely know where that specific system call
nstruction is.

It is very cheap code relative to the hurdle it provides.

ps. Another similar problem called "SROP" was fixed in 2016 for sigreturn(),
    using a similar idea of looking at the PC entry point.

As always, this is an excellent time to put the new code through the paces by installing snapshots (from a convenient-to-you mirror site) and testing in whatever inventive way you can come up with.

That way you help making the next release even better.

(Comments are closed)


Credits

Copyright © - Daniel Hartmeier. All rights reserved. Articles and comments are copyright their respective authors, submission implies license to publish on this web site. Contents of the archive prior to as well as images and HTML templates were copied from the fabulous original deadly.org with Jose's and Jim's kind permission. This journal runs as CGI with httpd(8) on OpenBSD, the source code is BSD licensed. undeadly \Un*dead"ly\, a. Not subject to death; immortal. [Obs.]