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)