Paper notes - Clean the Scratch Registers: A Way to Mitigate Return-Oriented Pro...
source link: https://dustri.org/b/paper-notes-clean-the-scratch-registers-a-way-to-mitigate-return-oriented-programming-attacks.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Clean the Scratch Registers: A Way to Mitigate Return-Oriented Programming Attacks
- Complete title: Clean the Scratch Registers: A Way to Mitigate Return-Oriented Programming Attacks
- PDF: 7c3954f7d19392056f072837b3a3775e
The latest Linux Plumbers Conference had a talk about security improvements in GCC, 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 by Zelin Rong, Peidai Xie, Jingyuan Wang, Shenglin Xu and Yongjun Wang.
The idea is to use Pintools 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 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, 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 or 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
- 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 (2020) or Loop-Oriented Programming (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 says:
This is used to increase program security by either mitigating Return-Oriented Programming (ROP) attacks or preventing information leakage through registers.
There is absolutely zero "information leakage" being prevented by this.
The patch adding this to the Linux kernel
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 from 2020, removing ROP gadgets doesn't equal to improved security, sometimes it's even worsening it.
Benchmarks are showing a ~1.5% performance impact when zeroing used scratch registers, and 17% for all* scratch registers. For comparison, PaX' RAP 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
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 that should have listened to Halvar's advice.
Thanks to Matteo Rizzo and others for proofreading this article.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK