I've been made aware of an ASLR mitigation technique known as "return to CSU". It builds on the existing idea of ROP gadgets, but instead uses the internal widgets available in __libc_csu_init().
This attack technique is not an exploit in itself but rather removing ROP widgets from predictable locations. Rather than waste more time, check out the information here, along with suggestions for mitigations/ideas to remove these enabling widgets.
So what I want out of this, I hear you ask. I defer to the experts on where to go from here, as this is not the area that I would know if any of these mitigations / plans have adverse affects.
It may be possible that the authors have already talked to the glibc maintainers, I did a quick search and wasn't able to find it.
Thanks in advance.
What I don't understand here is why moving the code to libc.so.6 helps. Wouldn't glibc then contain a very similar sequence? Certainly you'll need another round of probing to find the base address of glibc, but then you'd be in the same situation again.
We have looked at this code in an entirely different context (because it's compiled with different hardening flags compared to the main executable), so moving it may make sense, but it will make it impossible to run *any* binaries compiled against a newer glibc on an older glibc.
The actual ASLR bypass seems to be due to the forking server not ree-execve-ing itself after it observes a couple of crashing child processes. That allows you to learn the stack canary and base addresses for the executable and likely some of its libraries as well (and due to the constant offsets between libraries, that gives you the address of all libraries).
(In reply to Florian Weimer from comment #1)
> What I don't understand here is why moving the code to libc.so.6 helps.
The paper says this:
“Due to the size and complexity of the C library, it is generally considered a huge pool of gadgets. Once the attacker reaches the libc the game is over.”
So it's basically just obfuscation and not a fundamental change.
I posted a patch:
Flagging as security- because this is just hardening.
Thanks for the quick response ! patch looks good to me.
Any idea if this patch is being reviewed etc? I dont see anything on the mailing list that is why i asked :)
(In reply to Huzaifa Sidhpurwala from comment #5)
> Any idea if this patch is being reviewed etc? I dont see anything on the
> mailing list that is why i asked :)
There hasn't been a review yet.
New patch posted: https://sourceware.org/pipermail/libc-alpha/2021-February/122794.html
Hi Florian, Will this patch be synchronized to other glibc versions(glibc 2.28 etc)?
(In reply to lvying from comment #8)
> Hi Florian, Will this patch be synchronized to other glibc versions(glibc
> 2.28 etc)?
Unfortunately, a change like this is impossible to backport, due to its ABI implications.
Fixed for glibc 2.34 via:
Author: Florian Weimer <firstname.lastname@example.org>
Date: Thu Feb 25 12:10:57 2021 +0100
Reduce the statically linked startup code [BZ #23323]
It turns out the startup code in csu/elf-init.c has a perfect pair of
ROP gadgets (see Marco-Gisbert and Ripoll-Ripoll, "return-to-csu: A
New Method to Bypass 64-bit Linux ASLR"). These functions are not
needed in dynamically-linked binaries because DT_INIT/DT_INIT_ARRAY
are already processed by the dynamic linker. However, the dynamic
linker skipped the main program for some reason. For maximum
backwards compatibility, this is not changed, and instead, the main
map is consulted from __libc_start_main if the init function argument
is a NULL pointer.
For statically linked binaries, the old approach based on linker
symbols is still used because there is nothing else available.
A new symbol version __libc_start_main@@GLIBC_2.34 is introduced because
new binaries running on an old libc would not run their ELF
constructors, leading to difficult-to-debug issues.