ROP gadgets removal
As exposed in Is Less Really More? Why Reducing Code Reuse Gadget Counts via Software Debloating Doesn’t Necessarily Lead to Better Security by Michael D. Brown and Santosh Pande, removing ROP gadgets isn’t really a good metric to measure ROP resistance, if at all.
For those who know about polymorphism and pop/jmp or JOP, we believe once standard-RET is solved those concerns become easier to address seperately in the future. In any case a substantial reduction of gadgets is powerful.
This is likely why Todd Mortimer, the author of the aforementioned
RETGUARD, gave a
talk at EuroBSD 2018,
detailing his approach, which is more or less an implementation of a
subset/superset of the G-Free: Defeating Return-Oriented Programming through
paper from 2010.
On slide 13, the following is stated:
We don’t need to get to zero gadgets
❖ Just remove enough to make building useful ROP chains hard / impossible
❖ Use ROP tool output to measure progress
Unfortunately, since OpenBSD doesn’t implement
PAX_MPROTECT, an attacker usually needs only a couple of
gadgets to run the usual
read; write; mprotect; jmp payload, with a gadget to
dispatch syscalls, usually than a
dozen on Linux, and a bit more on Windows.
ret gadgets are removed via using two different means:
- Changing the register selection order in clang: this has no performances impact, and reduce the number of gadgets by 6%.
- Replace instructions that contain a
retby other ones (
xchg A, B; mov B, α; xchg B, Ainstead of
mov A, α.): negligible performance overhead, reduce the number of uniques gadgets by 5% according to Mortimer’s slides (slide 28) in 2018, but by 60% in 2019 (slide 51).
In February 2019,
this mitigation was improved, by prefixing gadgets with a
in their immediates or in their encoding, by a TRAPSLED (
jmp A; int3; int3; …
int3; A:) to prevent usage of misaligned gadgets.
This reduce the number of gadgets of 11%, leaving around 66,750 remaining one.
A quick look at Exodus Intel’s reports, metasploit modules, Project Zero blog posts or exploits, spender’s exploits, phoenhex’ writeups, ZDI’s blogposts, or even at exploit-db highlight that not a single exploit would have been made significatively harder to write, or less stale, if the number of gadgets was reduced by 11%.
But Theo de Raadt was convinced, in August 2018, that this was still a good idea:
In any case a substantial reduction of gadgets is powerful.
On arm64, since there are no misaligned instructions, every
is covered by
RETGUARD, which is discussed in a dedicated section further in
this article, but fear not, there are still plenty of JOP gadgets:
$ ROPgadget --binary ~/bsd --filter brk | grep Unique Unique gadgets found: 12891 $
Gcc used to have
but it’s now removed because “This option is fairly ineffective, and in the
light of CET, nobody seems interested to improve it. Deprecate the option, so
it won’t lure developers to the land of false security.“.
The ROP tools used to find gadgets are ropper, ROPGadget and pwntools, which is ok-ish, except that a metric used in the slides is
to ask ROPGadget.py to build a complete ropchain, and since it fails to do so
on a kernel with less rop gadgets, the mitigation is effective. This
affirmation is, given how
ROPGadget’s heuristics are, pretty amusing. Moreover, what would be the point
of building an
execve ROP-chain in kernel-land‽
Even if they changed their goal, with the objective of removing every single ROP gadget, and they magically achieved it, there would still be JOP, PCOP, COOP, … that aren’t significantly harder to use instead.
But removing ROP gadgets the way OpenBSD is doing it doesn’t add a large amount of complexity, doesn’t harm performances nor debuggability, so why not, but it doesn’t make exploitation significantly harder, at all.