10-year-old Sudo Bug Lets Linux Users Gain Root-Level Access (zdnet.com) 166
A major vulnerability impacting a large chunk of the Linux ecosystem has been patched today in Sudo, an app that allows admins to delegate limited root access to other users. From a report: The vulnerability, which received a CVE identifier of CVE-2021-3156, but is more commonly known as "Baron Samedit," was discovered by security auditing firm Qualys two weeks ago and was patched earlier today with the release of Sudo v1.9.5p2. In a simple explanation provided by the Sudo team today, the Baron Samedit bug can be exploited by an attacker who has gained access to a low-privileged account to gain root access, even if the account isn't listed in /etc/sudoers -- a config file that controls which users are allowed access to su or sudo commands in the first place.
Yet another null-terminated string bug... (Score:5, Insightful)
It's really time to put the idiotic null-terminated buffer paradigm behind the barn and shoot it in the head.
It's no longer 1972 where memory is so expensive that you have to scratch everywhere to save memory as much as possible!
What's wrong with strings (or buffers) with a separate length indicator?
Re: (Score:3, Insightful)
Re:Yet another null-terminated string bug... (Score:4, Insightful)
Well, the alternative to it is the length in front (aka Pascal strings) paradigm.
The problem with C "strings" is also a problem for all other C array allocations, that neither it nor the standard and practices evolved.
I'm not talking about garbage collection here. I'm talking about maintaining simple knowledge such as the size of that array allocation. I'm not talking about a language-enforced overbearing protection of memory with guards around every memory access, but come on...
The alternative as you call it, the length "in front" of the strings, is just a symptom fix. The problem is not knowing at all how big that memory block is after its allocated, which may at runtime eventually find itself to be larger OR smaller than what that string length field currently says.
Re: (Score:2)
You can use VLAs and pointer to VLAs and then arrays have the size information in C. You can then compile in run-time bounds checking (-fsanitize=undefined). .
int main()
{
int n = 3;
int x[n];
x[n] = 1;
}
x.c:6:3: runtime error: index 3 out of bounds for type 'int [*]'
Re: (Score:2)
Re: (Score:2)
Re: (Score:2)
Because that wastes about half of your RAM in the case where you CORRECTLY guess the maximum length that a user will need in the future.
Re: (Score:2)
Bacause you would require N versions of every function and structure that took a string. That would be a nightmare to maintain.
Re: (Score:2)
Re: (Score:3)
Wrong. Pascal used a LEADING BYTE for the length back then. It had to. Anybody suggesting wasting more than one byte in order to track string length would have been laughed out of room.
Re: (Score:2)
Most people use that strategy unwittingly, as most string-like things in most interpretations. I don't know of a huge outcry of how badly it sucks?
Contrast to having a sentinel value like '\x00' and it's more flexible, faster, and robust and is generally some implementation detail you don't have to think about.
Re: (Score:2)
faster
Depends on the use case. Calculating string length? Nope. Copying strings? Nope. Equal-comparing strings? Nope. Concatenating strings? Nope. Basically anything that requires knowing the string length is slowed down by needing to call strlen(), unless you cart around the string length independently, which a lot of C APIs require, including the highly common ISO C and POSIX APIs and even Win32 APIs.
Since you're carrying around the string length all the time anyway, it's faster and takes less space (even if
Re: Yet another null-terminated string bug... (Score:2)
I have played around with it, and it doesn't suck.
Got any actual arguments? Because right now, your lack of arguments is a good argument that you are wrong.
Re: (Score:2)
Well, the alternative to it is the length in front (aka Pascal strings) paradigm. Have you played with that one to see just how badly can that suck?
I have. It is pretty broken as well. You would have to put in 64 bit length fields to make it work generally, but then small systems may get a space problem and an efficiency problem. In addition, in a language like C, it will not be secure. A string is just an array in C and it will be very easy to accidentally write to the length field.
Re: (Score:2)
Re: (Score:2)
Forget C++. We are talking C here and for good reasons.
Also, there are really only two options: Make sure the length field is long enough to never be exceeded (not possible, see small platforms and memory-load) or to deal with it in every case (not possible because it blows up the respective libraries and comes at a performance penalty, see small platforms or really any platform).
In addition, any length field would need special protection (not possible in C).
Face it: This problem has no good solution. If th
Re: (Score:2)
Forget C++. We are talking C here and for good reasons.
sudo, an occasionally used system utility - what are the "good reasons"?
Make sure the length field is long enough to never be exceeded (not possible, see small platforms and memory-load)
Most C APIs that take an array argument also takes a length argument. If the implementation defined length is good enough for that system, then it's good enough. There's not problem with small platforms and memory load, because those platforms will have APIs that take the same field type.
comes at a performance penalty, see small platforms or really any platform
Calculating the string length all the time is a bigger performance penalty. You're ALREADY carting around the string length in a lot of code already. T
Re: (Score:2)
Forget C++. We are talking C here and for good reasons.
sudo, an occasionally used system utility - what are the "good reasons"?
Moving the goal-posts just says you have run out of arguments.
Re: (Score:2)
Seems like this is your tactic to avoid the fact that I shot down your other arguments.
Re:Yet another null-terminated string bug... (Score:5, Insightful)
> And there are literally zero downsides to length+bytes. None.
Except that a fixed length 'length' header means you're arbitrarily limited in string length. We can fix that with a dynamically sized 'length' header that uses a 'special character' to terminate the field. :)
Re: (Score:2)
Weird idea I just came up with: use a UTF-8 character for the length. It takes a little more calculation, and requires a lot of data be moved around if you want to promote the length in-place, but shorter strings would still have a one-byte length!
Realistically though, managing "strings" longer than 64K means moving around a lot of data, so a 16-bit length is probably good enough, and the extra byte isn't a problem if you have more than 64K of data space to work with. It'll also avoid address alignment pro
Re: Yet another null-terminated string bug... (Score:2)
Because of byte alignment requirements or performance issues, you're likely better off to just pad to the system word length or some ridiculous multiple like 64-bit for a 32-bit system word. Then if you align the length+data buffer, the data buffer is also aligned.
Re: (Score:2)
There are fast, well-known algorithms to do this, very similar in principle to UTF-8 encoding. They're fantastic for situations where most of your data is numerically small, but you occasionally need extremely large numbers as well.
https://en.wikipedia.org/wiki/... [wikipedia.org]
Re: Yet another null-terminated string bug... (Score:2)
Re: (Score:2)
An you want to mandate small, 8 bit systems using a 64 bit field here? Right....
Re: (Score:2)
Re: (Score:2)
intptr_t. uintptr_t. Or even ptrdiff_t.
So you want these small systems deal with overruns and longer strings differently than larger systems where the value range is just large enough to never be exceeded? Right....
Re: (Score:2)
Re: (Score:2)
What do you do if the available range is exceeded?
Re: (Score:2)
Re: (Score:2)
Re: (Score:2)
There are only so many bits available to use in the length field.
i.e. 32-bit length
Re: (Score:3)
Normally I would agree that would be a better choice but if you REALLY need strings more then 4,294,967,296 characters then you have bigger issues to worry about.
Re: (Score:2)
if you REALLY need strings more then 4,294,967,296 characters then you have bigger issues to worry about
Even if you need exactly 4,294,967,296 chars, that doesn't fit in 32 bits, you have a problem...
Re: (Score:2)
No it doesn't. Where on Earth do you get that from?
Well how big is your length field? Is it fixed (and going a "640k ought to be enough for anybody") or do you need to go all Xzibit and have a length field for your length field?
Re: (Score:2)
No it doesn't. Where on Earth do you get that from?
It does. And it is exceptionally obvious that it does. Unless you want to raise complexity even more by implementing it as a variable-length field?
Also remember that a C string is an array. It is very easy to accidentally write to the length field. Not good at all and hardly an improvement.
Re: (Score:3)
Because at the time null-terminated strings were considered, the other possibility was a leading *BYTE* that held the length, and thus the maximum string size was 255. Anybody thinking they would have ever considered using a larger size for the length does not know how incredibly expensive memory was back then. You would lose a byte if you used 16 bit lengths, and about 1/2 another byte per string due to alignment requirements.
Furthermore when we went from 16-bit int to 32-bit int, compatibility with existi
Re: (Score:2)
Right, because you're working on a 64-bit system.
Work on an 8-bit system as your string might be limited to a paltry 256 characters because that's the native int type. If that system needs to communicate with a PC (and they usually do, which is why you're using the 8-bit micro in
Re: (Score:2)
Re: (Score:3)
If I ever need more than 4611686018427387901 elements in an array of strings
That's about the length of my wife's "honey do" list.
Re: (Score:3)
And there are literally zero downsides to length+bytes.
Thats just not true.
One downside is that each string now take sizeof(length) - sizeof(terminator) more space, and if you still need the terminator anyways (a frequent situation) then you dont even get to deduct the terminator.
The upsides win, but this still isnt the whole solution because its the size of the backing allocation that matters most and you still arent recording it.
Re: (Score:2)
Re: (Score:2)
Re: (Score:2)
(For some sort of 8 bit embedded arch you're talking about one extra byte to give you a two byte length word, even then... not really a serious issue unless you're planning to have an enormous number of strings)
Maybe he's sending data using a USA based cellular network. :P
Those extra couple of bytes can cost hundreds of dollars!
Re: Yet another null-terminated string bug... (Score:2)
Another downside is that to get a pointer to the nth character of the string from a pointer to the string, you will need an extra add operation to add sizeof(length). So the machine code will be slightly bigger and slightly slower. Again, it may be worth it, but it is a downside.
You also lose the elegant way of getting a pointer to a string with whitespace (say) cut from the front simply by incrementing the pointer. Instead you have to copy all the data.
Re: (Score:2)
Well, you don't have to copy all the data. In c terms a replacement for string pointer would be:
struct {
size_t length;
char* ptr;
}
Not 'in front' really, just that a pointer is always in a struct with the length. So when you make your new pointer, then you have to make the 'pointer' bigger.
So a string is slightly larger, on performance some string operations will be faster (e.g. strlen() beecomes utterly trivial).
Re: (Score:2)
Again you are seriously deluded if you think a string with the overhead of the size of a pointer and possibly two memory alocations, as you suggest, would have ever been considered back then. There is not a chance anybody would have done anything as vastly wasteful as making all strings 4 bytes larger.
Stop looking at this from a modern perspective. The ONLY things tried were terminating bytes and a leading 1-byte length. Every other possibility you suggest was considered laughably inefficient and unusable.
Re: (Score:2)
Also, most C APIs have an extra length parameter for most array arguments to avoid always calculating the length, so you're going to be using up 4 bytes of stack every time the length is passed to a function, which is also inefficient
Re: (Score:2)
Try programming a PDP-11 with 16K of memory and get back to me.
The decisions made for C were really sensible considering what they would give up by adding the overhead of structures you suggest.
Re: (Score:2)
Re: (Score:2)
Re: Yet another null-terminated string bug... (Score:2)
Re: Yet another null-terminated string bug... (Score:2)
char *res;
if(res=strstr(object.string,"foo",object.len)){
something(res);
}
They say C lets you shoot yourself in the foot quite easily. "But I use a safe library" is the equivalent of "I swear the gun isn't loaded."
You still need to have your thinking hat on when you write C, and so does the kid fresh out of school on your team who might believe you that the library makes you safe.
Re: (Score:2)
"But I use a safe library" is the equivalent of "I swear the gun isn't loaded."
That's what you say, but the only example you gave was of a broken library.
Multics data storage (Score:2)
Care to expand on how Multics handled data ?
How was it to use ?
No overhead ?
Re:Multics data storage (Score:4, Informative)
Aside from what the teacher covered (he was all over controlling use of memory and pretty dismissive of malloc), not that much
There are a few things necessary for good security that people might not like today, like ACLs on every file, sole user ownership of files, no super user or administrator with access to other process 'rings', all memory written to disk as segments (another name for files)
It was considered large for systems of it's time, the complete distribution was about 6MB
MULTICS was in active US gov/mil use through the mid-90's, as a very secure system (result of 1972 AF tiger team actions) and improved hardware from Honeywell
It is telling that there is no public info on the successor to this system, but there are open source distributions of the code available now.
Re: (Score:2)
The problem is this software is rather old. Decades old, and were made during a time where RAM and CPU processing was expensive.
But also languages like C which are really low level where you have more control on what is written and how, then you have SUDO which is designed to break security. Creates a high risk application.
Oddly enough the C++ String options is a new feature in C, where people had to code for character pointers. And there is a lot of code written in that method.
Your fix, may mess up some
Re: (Score:2)
Unix (Eunichs see above) was born of Multics, which somehow avoided these pitfalls.
I do not see this as a generational error, but rather an intentional inclusion
Re: (Score:2)
This is a problem with Unix as well. As C/C++ managed strings the same way. You may have used a different language, COBOL, or FORTRAN which its compiler would deal with strings in a more robust way.
Re: (Score:2)
Multics was designed in Algol and written in PL/1, afaik
There was some feature of PL/1 that precluded pointers, that may have been the heart of the matter
The teacher had a funny way of approaching things. To teach about buffer overflow, he asked us to include laying a book on the keyboard as a test case.
This was in the early 90's (can't remember the teacher's name, and he was retired at that time). I moved on to pl/sql (a descendant of ADA) in the late nineties and have never looked back
Re: (Score:2)
true enough, but I have to wonder why people still install fingerd, which has the same exploit and provides zero value
hardening-check (Score:3)
I know that the article specifically says that ASLR was defeated, but I wonder if these other compiler/linker mitigations prevent (some of) these vulnerabilities (specifically fortify)?
The "hardening-check" perl script is available from EPEL on redhat platforms. Here I use it to report mitigations in an old FWTK component that I use for an internal legacy system.
Re: (Score:2)
Um, I work on systems where memory is tight and you can't just bloat things up by hiring the cheapest developers. I've worked on things with 384 bytes, 256 bytes, 20K bytes, 1M bytes, etc.
Buffers with a length indicator have their own sets of problems and security bugs just as bad as null terminated string does. Nothing fixes the problem except to spend money to examine code carefully by developers trained in how their system, language, and libraries work.
Re: (Score:2)
What's wrong with strings (or buffers) with a separate length indicator?
The problem is typically less about memory consumption, and more about computational efficiency. The bounds checking and updating isn't free, and if you're doing a massive amount of string work, that can add up quickly.
Of course, programs that need that kind of string efficiency are the exception, and not the rule -- so in general I agree (as do most modern languages, which do have bounds-checked strings). But you're going to have a really hard time converting billions of lines of legacy C code to use a n
Re: (Score:2)
I think a good first step is to audit security critical software. This seem what happened here.
Then one could rewrite security critical code to use safe string libraries which should be far less work than rewriting everything in a different language.
Re:Yet another null-terminated string bug... (Score:4, Insightful)
Fundamentally sudo is a rare operation. There is no reason to implement this in C. Let's implement that in a language with proper memory safety. Or at least in C++.
I am usually not one to call to rewrite operating systems in languages like go and rust. The overhead can kill you. But how often is sudo used? Very rarely, if you call sudo all the time, there is something fundamentally wrong in your infrastructure. Since it is not called often, it is not time-critical. So let's write it safely.
If it were the IP stack, I'd be a bit more concern in rewriting it in a possibly slower language because it is on the critical path of many things. But sudo, not time-critical; we should probably rewrite it.
Re: (Score:3)
Re: (Score:3)
Back in 1972, a null terminator or a string length indicator took the same amount of memory. Strings would be limited to whatever the smallest word le
Re: (Score:2)
Grr. I meant
Re: (Score:2)
Buffers with length indicators are not inherently more secure in a language like C. The real problem is careless coding and coders that over-estimate their skills and hence are not careful enough.
On the plus-side, "sudo" is basically a finished product and eventually, it will be bug-free. Unless the systemd-morons decide to re-implement this one, badly, as well.
The FUCK (Score:3, Insightful)
Re:The FUCK (Score:4, Informative)
Privilege escalation bugs are common in all operating systems. The paradigm of "we want to let you do everything but not really everything" is a hard one to implement in a secure way.
Remote exploits are easier to defend against.
Re: (Score:2)
This isn't some obscure type of vulnerability, it is a long known and understood one and has been sitting in plain sight in one of the most critical components
If sudo is one of the most critical components of your OS, you've done something else wrong, something you can't blame on the developers.
Re: (Score:2)
Well, one reason could be that people who take security seriously were already omitting sudo from their installs. The fact of the matter is that it's by it's very nature a giant hole drilled right through your system security barriers, even on a good day.
Re: (Score:3)
Re: (Score:3)
Concurrence except for "only". Some of the eyes are attackers who don't want to modify the code but seek to find exploitable weaknesses like this one. Perverse incentives strike again.
What always scares me about deep-bug stories like this one is that we'll probably never know when it was first discovered. We only get the bad news early in the cases where the first bad guys were clumsy and got caught.
Disabling sudo access was the old fix. (Score:4, Insightful)
Multiple eyes doesn't mean anyone can be arsed to do the chore of fixing what the eyes find.
Re: (Score:3)
Re:The FUCK (Score:4, Interesting)
Answer: C.
How are people still surprised by this? It's a classic buffer overflow. C is insanely dangerous, and entire OSes are written in it. Any tiny mistake can mean a major memory-related security issue. There are likely dozens, even hundreds of equally serious bugs in any given major OS that simply haven't been noticed yet.
Unfortunately, there isn't really a good solution for this. It's not exactly practical to replace a million lines of C code with Rust or whatever. C++ is a bit more compatible, but still isn't memory-safe (even if better than C), and Linus hates C++ besides. It's been decades and apparently tools still don't exist to catch stuff like this, or at least, not ones the Linux team are using. We'll likely hear from the "git gud" brigade, telling us that we just need perfect programmers who never make mistakes, but that's pure fantasy.
Obviously, memory safety alone won't fix all bugs. But it absolutely would have prevented this one. Both Google and Microsoft have performed audits on their own code that indicate over two-thirds of security issues are memory-safety bugs. I'd love for someone to perform a similar audit on Linux-related CVEs, as I bet we'd see roughly the same proportion.
Re: (Score:2)
Re: (Score:2)
Re: (Score:2)
Can't speak to Rust, but I find go a nice enough language and better than some alternative strategies for runtime, but the general anti-dynamic linking mindset can be challenging in a lot of contexts.
Re: (Score:3)
Rust isn't memory safe either.
I presume you mean when in "unsafe" mode? It's a little disingenuous to call Rust out on this, as that statement requires a giant asterisk. I mean, by that definition, C# isn't a memory-safe language either.
The entire point of an unsafe mode is that you should be able to easily audit the code that is required to be labeled as such. I'm not a Rust programmer (just dabbled a bit to see what it was about), but if it's anything like C#, you generally require very few lines of unsafe code, and most likely, it
Re: (Score:2)
a) It got found, even if late
b) Closed-source will be much worse
Re: (Score:2)
doas (Score:5, Informative)
Re: (Score:2)
Still waiting for persistence outside of OpenBSD but yeah doas is a solid alternative.
Re: (Score:3)
Re: (Score:2)
This is why my servers run doas instead of sudo.
Pfft amateur. Real men just log in as root and don't ask the system to pretend to be someone else. Sure there's the issue that when you create files your daemons can't read them, but the solution there is to simply run all those critical services as root too.
Some people say it's a security risk, but my servers are running great. Even my ISP sends me daily congratulatory emails about how much traffic my mail server is handling. Though for some reason they are threatening to cut me off. They must be jealous.
Re: (Score:2)
login as root?
Why, when toor [wikipedia.org] is infinitely superior?
Poor security design to start with (Score:4, Interesting)
sudo is just another example of the giant (intentional) hole in the security barrier that is setuid root.
Just because of poor delegation design to start with, the setuid bit was invented instead of changing the design to a proper fine-grained access control. Instead of having proper access control lists for all resources like e.g. printers or privileges to set the system time or time zone, Dennis Richie though that setuid was such a neat idea the he went and patented it.
setuid root elevates the process itself to the all-powerful root account as the effective user. This means that all existing access control is moot. An audit of the access control setup can never reliably list what a user is allowed to do on a system. The existence of just a single setuid root tool means that you will need to know what that tool can do intentionally or unintentionally because of a bug as in this case.
a setuid "service" is not a real service with a limited and discoverable surface which can be independently secured, like e.g. a proper daemon with which you communicate through interprocess means. It is a process of which a would-be attacker has tremendous control the environment and parameters. To know what a setuid tool can do you need to read the source code and be able to verify that the tool is indeed the correct manifestation of the source code.
Lack of tools (Score:2)
Automated "fuzzers" and static analyzers do help up to a point. However when C is used as a "slightly higher level assembly language", and most programs have micro-optimizations they can help so far. Basically you either get too many false positive warnings, or don't get enough at all.
We are lucky this bug was fixed. But you can be sure many others are lurking in unsuspecting tools.
That is why for example, we abandoned OpenSSL and replaced with LibreSSL, which heavily drops unused parts to reduce potential
Re: (Score:2)
However when C is used as a "slightly higher level assembly language"
Its neither that nor the opposite problem ("slightly lower level high level language.")
C is, was, and always will be a high level language. In spite of the fact that it is a high level language.
C has pointers. Pointers dont make it low level language (even BASIC can peek and poke!), but they do introduce issues when they are used. It is specifically that neither the language proper nor the standards and practices of its practitioners have evolved to aid in those issues.
There are different ways to hel
Re: (Score:2)
C's history is based in B (which was based on another language called BCPL), and B was designed to be a machine independent system language.
https://en.wikipedia.org/wiki/... [wikipedia.org]
It did not have types, and everything was a system "word" (auto). You could multiply or dereference the same variable. If you look at the examples, some of them are still valid K&R style C programs.
Over time C became higher level (dynamic arrays, and whatnot). However the low level roots are still there.
Not a problem for almost everyone. (Score:2)
If you got user access, you already got everything you need.
And when was the last time you were logged in on a shell as a user on a multi-user server? They all give you some kind of virtualized encapsulated container.
I mean we're not using mainframes with terminals anymore. (Or the webCancer variant.)
No matter how much the Silicon Valley mafia wants that again.
Re: (Score:2)
BAReFO0t, please get professional help. Both for your mental condition and for someone to apply updates on the server you just announced to the world you're neglecting.
Cue all those that do not understand the issue (Score:2)
There are a number of reason this is done in C this way. One is to keep the run-time system small. Another one is to be space-efficient. Another one is to not impose arbitrary length restrictions. Another one is that an explicit length field would be overwritten just as easily. I am sure there are more.
If there were a really superior solution, it would have been moved into the C standard library a long time ago. There is not. The only thing that helps is careful coding.
intptr_t ought to be enough for anybody. (Score:2)
use FreePascal if you want to avoid this (Score:2)
If this was written in Pascal, it wouldn't have had this exact bug, it would have been a different bug.
Too bad Pascal was considered a toy "teaching language" and we went with a high-level wrapper around a rather clunky assembly-language variant. (I'm a C programmer and have been doing almost 100% C for the past 30 years)
We could write something like sudo in Rust or Go pretty easily. Of course we'd have to update the program every time the language changes too much. But probably a small price to pay in a fa
I guess at least we can remove it (Score:2)
I guess at least we can remove sudo if we don't use it, rather than being a built-in part of a more monolithic OS. That's what I've just done to my production servers.
Maybe it shouldn't come as standard for any server oriented distro through, is there even any advantage to using sudo over adding users to the root group in server use cases?