Glibc dynamic loader hit by a nasty local privilege escalation vulnerability
phoronix.com
phoronix.com
GLIBC_TUNABLES is apparently an environment variable that lets you turn various performance knobs affecting glibc internals: https://www.gnu.org/software/libc/manual/html_node/Tunables....
OK, so obviously someone calling a SUID program shouldn't be able to set these options at all, right? So I guess the bug must be that they forgot to clear this env var when starting a suid program? Given that there have been so many vulnerabilities like this you'd think they would have really audited environment variable usage to avoid this but maybe it slipped through?
... NO WAIT! It's worse! They apparently intentionally allow some tunables to be tuned across a SUID boundary. Looking at the commit that is blamed for introducing this problem, it is explicitly dealing with the filter logic for tunables in SUID mode!
https://patchwork.ozlabs.org/project/glibc/patch/20210316070...
But whyyyyyyy? Why not just ignore it altogether? Is the use case for tuning ultra-advanced performance settings across SUID so strong that it outweighs the obvious security risks?
I mean, I'll admit, I've never seen this env var before, I have no idea how it's used and whether there's actually a good reason why you'd want to use it over SUID. But boy this seems like a hugely risky feature and sure enough... it broke.
GLIBC_TUNABLES=glibc.cpu.hwcaps=
-AVX
-AVX2
-AVX_Usable
-AVX2_Usable
-AVX512F_Usable
-SSE4_1
-SSE4_2
-SSSE3
-Fast_Unaligned_Load
-ERMS
-AVX_Fast_Unaligned_Loadmusl (used by default on Alpine and Chimera Linux) and the BSD libc’s on the other hand are much more minimal and conservative.
Read: slower and with less comprehensive standards compliance (even if some of those standards are a bit nutty).
the rest is mostly fewer specialized implementations of stuff for different cpus, but that rarely makes a difference in practice (i.e. outside of microbenchmarks)
the overall standards compliance is pretty good in musl
Also available on Void Linux.
What are you basing this on? Can you demonstrate how this works?
The "house of" attack method are attacks against the allocator, its been a while since I've looked into it, I hope musl have hardened their allocator against this kind of attacks.
Musl was not being used by the business I worked in, therefore I had no interest in continuing investigation other than trying to get an understanding of how other implementations dealt with it.
I'm sorry for any confusion.
Why is this even an attack and why would musl have whatever weakness is claimed here?
hah.. even worse, if you follow down the thread in your last link, apparently there is an SXID_IGNORE which they decided was better implemented as an SXID_ERASE for some variables.
for each GLIBC_TUNABLES that it finds, it makes a copy of this variable (at line 284), calls parse_tunables() to process and sanitize this copy (at line 286), and finally replaces the original GLIBC_TUNABLES with this sanitized copy (at line 288)
To sanitize the copy of GLIBC_TUNABLES (which should be of the form "tunable1=aaa:tunable2=bbb"), parse_tunables() removes all dangerous tunables (the SXID_ERASE tunables) from tunestr, but keeps SXID_IGNORE and NONE tunables
...already made me expect what I'd see, and indeed the code there is quite unnecessarily complex. The first simplification I'd do is not make a copy of the variable's value, and the second one is to use the standard C string functions extensively; it's notable that there's no occurrence of strchr() anywhere in that function, despite it being a very useful function for parsing key-value pairs.
https://news.ycombinator.com/item?id=37754973
I guess this isn't entirely a duplicate thread though.
That one is fixed in Debian stable (and probably others):
The relevant code is a textbook example of how to make an inscrutable mess of string processing in C. You can do better, even in C, with some care and better hygiene. But the language does not prevent you (or hell, even discourage you) from creating such abominations.
The bug was introduced in glibc 2.34 so I'm guessing RHEL 7 (glibc-2.17) and RHEL 8 (glibc-2.28) are not affected. That just leaves RHEL 9 which is running glibc 2.34.
As most genuine multi-user setups are probably running EL (at least, nearly all the ones I've ever used...), this is a pretty big silver lining...
Ok, I'll drop the snark. Do you know of any distributions that approach your idea seriously and ships absolutely no setuid binaries? Ideally they also make sure the user won't install an external package that ships setuid executables.
It's certainly technically possible, you just need to mount everything (including `/`) as nosuid. But even `su`, `sudo` and... `pkexec` need suid to work. So I don't think it's easy to have a usable linux environment with zero suid binaries. Or am I wrong?
In case of this bug, since the bug is in ld, my educated guess is that even one suid binary is be enough for the privilege escalation attack to work.
[1]: https://github.blog/2021-06-10-privilege-escalation-polkit-r...
Also, side rant, su/sudo were developed from what i would call being lazy (uhg i dont want to open a new terminal to run a command as root) which has led to a lot of abuse. But, i blame ubuntu for its widespread use and abuse, the first user made by a ubuntu install has full sudo privs out of the box. And then, every guide thereafter just became "just sudo su" (which uhg, in itself). The lazy factor by sudoing everything has led to uses of sudo i wish id never seen. I wish we never had it.
I contest that reputation exists in anything but your own mind or bubble.
If all of glibc had been written in assembly for every single target triplet, one wouldn't be wrong to point out that there was no benefit to doing that instead of writing it in C, and that it probably took more work and was more error-prone, even if they weren't willing to help port said code to C.
Just the same, they could have written this in C++.
Much of LLVM's libc is written in C++ with exposed C bindings (or all, I haven't checked 100%). For example, their libc stdio implementation is completely in C++.
https://github.com/llvm/llvm-project/tree/main/libc/src/stdi...
You could say the same thing about Rust and Relibc, but it seems much less likely that a C project would incorporate Rust than that it would incorporate C++.