Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Glibc dynamic loader hit by a nasty local privilege escalation vulnerability (phoronix.com)
140 points by mfiguiere on Oct 3, 2023 | hide | past | favorite | 66 comments


Looking at the details here I have to say it sure looks like the glibc people were asking for trouble.

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.


The GLIBC_TUNABLES can be used to "choose" between different versions of e.g. memcpy at runtime. This can be really useful for debugging, if you want to allow/disallow AVX extensions, for example (even if your CPU supports them). This is what I use on x86_64 to make life easier for my "static" version of qemu usermode:

  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_Load


Does removing ERMS actually make life easier? I thought it was the "do the simple thing" option, and an emulator could just turn it into a memcpy on the emulator's side.


rep movsb has stronger requirements than memcpy, in particular with regards to behavior when the source and destination overlap.


Sounds like a DoS vector at least, even if the filter worked correctly. Why should you be able to disable AVX extensions for another user's process? Why would you debug across SUID boundaries?


From what I’ve read from some security researchers, glibc is not really focused on security. For example, it contains CPU-specific manually crafted assembly for optimization purposes, and its memory allocator implements exploitation-friendly features such as thread caches. I’m not surprised by this vulnerability, and I suspect there are many similar ones that haven’t been discovered yet.

musl (used by default on Alpine and Chimera Linux) and the BSD libc’s on the other hand are much more minimal and conservative.


>musl (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).


I was under the impression that musl does care about standards compliance, hitting problems mostly when people expect non-standardized behavior.


Musl is somewhat the embodiment of malicious compliance to the standard.


musl has a better track record of standards compliance than glibc. Common method for porting to musl is putting non-standard compliant code into an ifdef glibc.


This is, of course, the same musl that refused to implement TCP DNS requests until very recently. And they only did so because the DNS standards people got fed up with people not implementing it, and made it a requirement.


It was made a requirement by RFC 7766 in 2016 https://www.rfc-editor.org/rfc/rfc7766#page-6


That doesn't necessarily contradict "the DNS standards people got fed up with people not implementing it, and made it a requirement."?


It took musl about 6 years to implement the requirement.


And glibc is the one not implementing strlcpy and strlcat until June 2023.


strscpy is the better API anyway


I do not know about standards, but I know the only deployment images I have problems all the time with random weird DNS failures are the musl-based ones. No idea what causes it, but makes me wish to get rid of all musl usage just to have stable deployments.


Counterexample: musl's crypt() does silly things, like treat all unrecognized inputs as DES, even if the salt characters are invalid.


at least it actually supports blowfish


pretty much the only place where it's meaningfully slower is the allocator (which has a massive impact) so in chimera we have a special musl build that uses llvm's scudo instead (same as e.g. android) and is faster as well as more secure than glibc's allocator

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


Still there's a difference between not doing the maximum possible to prevent theoretical exploits (like caches thing) and adding an externally controlled feature that crosses into SUID territory and begs to be exploited. It's like the difference between not buying the door made of strongest alloy steel and just leaving the key under the potted plant. The latter makes you say "what were you thinking?"


"musl (used by default on Alpine and Chimera Linux) and the BSD libc's on the other hand are much more minimal and conservative."

Also available on Void Linux.


Scroll through some musl code if you have some spare time, they take code golfing with C strings to a whole new level. What saves them is only that they skip the insane stuff in glibc.


musl's code is hardly pretty, but considering every linux libc's code is cursed and especially glibc's, musl still more or less wins there


Last time i looked, the musl library allocator was also exploitable by similar style 'house of' attacks. Maybe its been fixed now. I dont know if you can make a judgement call that musl is better because it has 'less code' when its still open to exploit mechanisms that glibc fixed many moons ago.


the musl library allocator was also exploitable by similar style 'house of' attacks

What are you basing this on? Can you demonstrate how this works?


Not in a HN news comment, I don't have that level of dedication to this because its definitely non trivial. However you can read about them in use against the glibc allocator are here: https://github.com/DhavalKapil/heap-exploitation/blob/master...

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.


The link you have here is 'attacking' by directly changing the meta data in C with pointer offsets and doesn't mention musl anywhere.


I did not say that it did. I'm saying that I believe the same kind of attacks work against the musl allocator, or at least I think they did at that time.

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.


I believe the same kind of attacks work against the musl allocator

Why is this even an attack and why would musl have whatever weakness is claimed here?


Gotcha.


> Why not just ignore it altogether?

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.


https://www.openwall.com/lists/oss-security/2023/10/03/2 is probably a better link (without ads and unnecessary images), which is essentially what the article is citing 1:1.


Reading these parts of the analysis...

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.


strtok would be the function to use, it’s silly to open-code it like that


Yeah I linked to that same writeup in this earlier piece:

https://news.ycombinator.com/item?id=37754973

I guess this isn't entirely a duplicate thread though.


Looney Tunables: Local Privilege Escalation in the glibc's ld.so (CVE-2023-4911)


Are you sure it's the correct CVE?

That one is fixed in Debian stable (and probably others):

https://security-tracker.debian.org/tracker/CVE-2023-4911


Yes, that's the CVE referenced by the Phoronix article and the oss-security post. The researchers coordinated their disclosure with the security teams from the major Linux distros, so packages with the fix should be available for most of them today.


But then this link is unreadable on a phone since the text doesn't reflow


Yikes.

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.


It is a failure that setuid executables still exist in modern Linux.


But it's possible to minimize privileges across binaries with that. I think the possible preventable failure is having too much code within setuid/root bounds.


Small silver lining for the folks here that manage Red Hat Enterprise Linux systems.

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.


(and it's not obvious if 2.34 is easily exploitable either, though it has the same bug, as it uses sbrk instead of __minimal_malloc. So at least the exploit here won't work, though maybe there is another one).

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...


RHEL 8 is impacted, the fix that introduced the issue was back ported:

https://access.redhat.com/security/cve/cve-2023-4911


The problem is setuid. Just get rid of that and this becomes irrelevant.


The problem is multiple users. Just get rid of that and this becomes irrelevant.


The problem is computers. Just get rid of them and this becomes irrelevant.


Well, there's polkit now to avoid granting such broad permissions via setuid, so it's not like there isn't a viable alternative right now.


Fortunately polkit can't be abused to do something like privilege escalation, right [1]?

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...


Assuming a sufficiently secured access method via SSH (ex strong ciphers only, key only, etc), you can get by just fine without su/sudo, of course, youll have to switch users manually, by logging in as a root-like user (ex root aliased, or root itself). Services and such can still operate as other users, as (as far as i know) systemd and rc.d dont rely on suid.

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’m not saying polkit isn’t an improvement, but it also isn’t a panacea: https://github.blog/2021-06-10-privilege-escalation-polkit-r...


Additionally writing security rules in JavaScript just feels wrong. And alienates users who simply want to mount USB drives without root.


Better than writing them in the sudoers language, whose definitions are annotated...


Yeah, viable, but far from satisfying. I'd say polkit is a bigger pile of trash than setuid is.


And some people say string handling in C is easy. Maybe using memory unsafe languages for anything security related isn't such a good idea after all…


Feel free to replace libc with a memory-safe implementation.


The vulnerable piece of code didn't need memory unsafety. Sure, __minimal_malloc does, but the bug was in mundane string handling.


libc would be a pretty good place to drop a memory-safe replacement, honestly.


Have a look at Eyra (or more specifically c-ward).

https://github.com/sunfishcode/eyra/

https://github.com/sunfishcode/c-ward


That dismissive attitude gives the free software community its bad and well-deserved reputation. There are reasons why some of glibc is written in C or assembly, but 99% of it is just untested neckbeard bullshit that is easily replaced by something safer. There is no legitimate reason why glibc parses environment variables this way.


> [...] its bad and well-deserved reputation.

I contest that reputation exists in anything but your own mind or bubble.


easily done by _someone else_, I assume?


The fact that they haven't rewritten glibc's string parsing wouldn't make their criticism wrong.

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++.


This would need a memory-safe POSIX, which does not exist. Not even proper strings.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: