Title: Paper notes - Clean the Scratch Registers: A Way to Mitigate Return-Oriented Programming Attacks
Date: 2021-10-05 22:00

- Complete title: Clean the Scratch Registers: A Way to Mitigate Return-Oriented Programming Attacks
- PDF: [7c3954f7d19392056f072837b3a3775e]({static}/files/papers/7c3954f7d19392056f072837b3a3775e_rop_reg_clean.pdf)

The latest Linux Plumbers Conference had a talk about [security improvements in
GCC]( https://linuxplumbersconf.org/event/11/contributions/1001 ), and one of
the new mitigation is "call-used registers wiping on return", based on the
paper "Clean the Scratch Registers: A Way to Mitigate Return-Oriented
Programming Attacks", published in
[2018](https://ieeexplore.ieee.org/document/8445132) by Zelin Rong, Peidai Xie,
Jingyuan Wang, Shenglin Xu and Yongjun Wang.

The idea is to use
[Pintools](https://software.intel.com/content/www/us/en/develop/articles/pin-a-dynamic-binary-instrumentation-tool.html)
to monitor the execution, and to:

- flag written-to scratch registers;
- reset the "used scratch registers" flags on every `call`;
- clear flagged scratch registers on every `ret`.

> We ran the programs under the
instrumentation of the system and then ran the exploit scripts
to attack them to see whether we can get the shell.
> […] we picked up 7 typical
shellcodes and rewrote them in ROP with the gadgets which
generated by ROPGadget

This is utterly ridiculous: Nobody is using
[ROPGadget](https://github.com/JonathanSalwan/ROPgadget) to generate rop-chains
for real-world exploits. I've never seen been used for this purpose seriously
by anyone, not even in CTF. Its algorithm is
[ultra-naïve](https://github.com/JonathanSalwan/ROPgadget/blob/master/ropgadget/ropchain/arch/ropmakerx64.py),
and can be considered as a toy or a proof of concept at best when it comes to
automatic exploit construction. And even when used as a pure ROP-gadget finder,
it tends to miss a ton of them compared to [ropper](https://scoding.de/ropper/)
or [ropr](https://github.com/Ben-Lichtman/ropr).

Moreover, checking that trashing the registers on every `ret` will **of
course** break existing exploits written without this "*mitigation*" in mind.
One could simply recompile stuff with debug options, and it'll
break a ton of exploits by simply changing the hardcoded ROP offsets as a
side-effect. This is a dumb metric.

Anyway, I see at least four low-hanging *generic* bypasses, beside using `JOP`/`COOP`/`SROP`/…

- In user-land, using the [libc one-gadget RCE](https://j00ru.vexillium.org/slides/2015/insomnihack.pdf)
- Having gadgets ending with `call…ret` to reset the "scratch registers marking".
- Use functions are gadgets, as done in [On the Effectiveness of Type-based Control Flow Integrity](https://arxiv.org/pdf/1810.10649.pdf) (2020)
	or [Loop-Oriented Programming](https://ieeexplore.ieee.org/document/7345282) (2015)
- Do ROP without using scratch registers. It's trivial in kernel-land: zero
	your credentials, overwrite `core_pattern`, overwrite your tasks's credential
	pointers with `&init_cred`, … and should likely be doable in userland as
	well.

Of course, more specific bypasses exist, but they're more binary-specific.

> It is noteworthy that our system can handle the situations like `setjmp`,
signal, exception and lazy binding that some dynamic detecting solutions can’t
handle.

Since their *system* doesn't do anything against `SROP`, it's not really
noteworthy at all.

> The average performance overhead is nearly 16.2 times

With the lowest impact (gzip) being 4900%, and the highest (firefox) 25000%,
this is *completely ludicrous*. But things get even worse: the GCC
implementation does the following:

> In this new pass "pass_zero_call_used_regs": scan the exit block from backward to look for "return":

This means that unaligned gadgets aren't taken care of at all.

The [commits message](https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557568.html) says:

> This is used to increase program security by either mitigating
Return-Oriented Programming (ROP) attacks or preventing information leakage
through registers.

I fail to imagine a single situation where any kind of "information leakage"
would be prevented by this scheme.

The [patch adding this to the Linux kernel](https://lore.kernel.org/lkml/20210505191804.4015873-1-keescook@chromium.org/)
also uses ROPGadget to try to generate a complete (userland) `/bin/sh` popping rop-chain, sigh.

> Additionally this helps reduce the number of useful ROP gadgets in the
kernel image by about 20%:

As demonstrated in [Is Less Really More? Why Reducing Code Reuse Gadget Counts
via Software Debloating Doesn't Necessarily Indicate Improved Security](https://arxiv.org/abs/1902.10880) from 2020,
removing ROP gadgets doesn't equal to improved security, sometimes it's even
worsening it.

Benchmarks [are showing a ~1.5%](https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553212.html) performance impact
when zeroing *used* scratch registers, and **17%** for *all* scratch registers.
For comparison, [PaX' RAP](https://pax.grsecurity.net/docs/PaXTeam-H2HC15-RAP-RIP-ROP.pdf) has an impact of ~7%,
and is killing code-reuse attacks ~completely.

`-fzero-call-used-regs`, which has 9 (!!!) different options looks a lot like
the [deprecated `-mmitigate-rop`]( https://patchwork.ozlabs.org/project/gcc/patch/CAFULd4ZL-wa3wBaH4cNmvW=nxMYDgXtybinE4sg5EmsAgtasMA@mail.gmail.com)
gcc option: useless at best, providing a false sense of security with a maybe
possibly small performance impact at worse.

tl;dr yet another example of [MitiGator](https://twitter.com/halvarflake/status/836492121339211776)
that should have listened to [Halvar's
advice](https://addxorrol.blogspot.com/2020/03/before-you-ship-security-mitigation.html).

Thanks to [Matteo Rizzo](https://twitter.com/_matteorizzo) and others for proofreading this article.

