Look, I don't want to keep talking about security issues. Blame AI. Just another thing it ruined.
At this point, you've probably heard about *sshole. If you haven't, the quick summary: on some Linux systems, SSH will log you in to an account with no password if you supply a password greater than 256 characters long. Frequently, you might make accounts like root nominally inaccessible by giving it no password, so this is a big deal. Worse, it impacts varieties of Linux that use the musl implementation of libc, like Alpine- the kinds of varieties that run inside a container hosting a web app somewhere.
The impact is broad, and is definitely being exploited. The root cause, however, is interesting, because it's a sum of very simple choices that lead to a wildly undesirable result.
The core function in question is crypt, a stardard part of the C library. crypt is already not the best name, as it's a hashing function, but its behavior is pretty clear. Pass it a string and a salt, and it gives you a hashed version of the password suitable for storing in your /etc/shadow file.
Now, the man page has the answer to the important question: what happens when I hand the function invalid inputs, like a bad salt? In the glibc implementation, it returns a NULL. That's a pretty standard C-style method of handling errors: I failed to do the thing, so I'm going to return a NULL value. This puts the onus on the callers to correctly handle the NULL (which also handles the error).
glibc isn't the only implementation of libc, though. There are many others, each tuned to different types of runtime environments. For example, uClibC is a minimal implementation specifically designed to make lightweight executables that can run in resource constained environments. It makes tradeoffs that make it less desirable for a desktop or server environment. musl attempts to do both: be lightweight and suitable for embedded environments and also suitable for server environments. This makes musl a great choice for building Linux distributions meant to run in a container, which again, is why this bug is turning out to be such a problem.
musl does not return a NULL when it fails to hash your input. Instead, it returns a value that could not possibly be a valid hash: *. Here's their sha256 implementation where you can see that behavior. It's "fine", because you can still check the return to see if you errored: it's too short to be a valid hash, it's an easily identifiable character.
But why deviate like that at all? Well, error handling is hard. Null handling, done incorrectly (or in many real world cases, not at all), leads to crashes. To keep callers happy, the musl team decided to not return NULL, and instead return an invalid string. There are many other choices they could have made, but that was the choice that they made.
There's just one problem: in your /etc/shadow or /etc/passwd files, when you want to ensure an account can't be accessed via passwords, you also set their password hash value to an unhashable string: "*". Again, there are many other choices that could have been made, but that's the choice that was made.
There's now an obvious ticking time bomb here. If an attacker can somehow supply a string that triggers the invalid code path, crypt will return "*". When a program, like OpenSSH, then checks the shadow file to see if the hash matches the saved one, the answer for any account without a password set will be "yes, this matches, "*"=="*"."
How can the user reliably trigger this? Well, doing hashing can be expensive, especially if you're doing something like SHA-512, so the developers put a cap on how long the input string is allowed to be. That cap was 256 characters. If the user supplies more than 256 characters, hashing fails, and when hashing fails, we return... "*".
Et voila, when you pass a musl-linked SSH a password longer than 256 characters, and the account you're logging in as doesn't have a password, you can log in. And again, many containers are running with the musl c-library, which means many containers are impacted by this. It's going to be a huge mess.
And in the end, of course it's null handling. Of course the billion dollar mistake rears its head again, in a completely new and unexpected way. And of course, this flaw is part of the ongoing "Black October" as LLMs turn finding software bugs into a function of compute power, and not a function of human effort. There's going to be a lot more of this.