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.
Nonetheless, Theo de Raadt said the following in 2017, about RETGUARD:
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 (except in
mode of course), every
ret instruction 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 $
There are of course more instructions/gadgets
that aren’t covered by
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.
In 2023, De Raadt sent a snarky email
on openbsd-tech, about OpenSSL having
0xC3 in some assembly routines:
Far be it from me to suggest that the security experts over there in OpenSSL land are unaware of modern exploitation methods! Very far from that, very very far.
So apparently Joan Daemen and Vincent Rijmen
are playing the long game with shellcode/gadgets in s-boxes!
Albeit to be fair, those s-boxes should be in the data-segment, but it’s hardly
security-relevant. Moreover, OpenBSD also has/had
.text, so nothing to brag about.
Anyway, 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.