Does this really work? The command is supposed to copy the original file to a temporary file, run the edit command with the privileges of the original user and then copy the edited file over the original. Otherwise what’s stopping an attacker from telling the editor to just open another file?
Yeah I had the same confusion, the linked PDF explains it. Basically sudo determines the list of files to edit after expanding the `EDITOR` variable into separate arguments, and the `--` in the argument list (added by `sudo`) is used to determine where the file arguments provided to `sudoedit` start in the new argument list.
By adding your own `--` in the `EDITOR` variable, `sudo` gets confused and thinks that `--` is the start of the `sudoedit` file arguments and thus happily copies and edits all the files after it.
Incredible! So the problem is not -- but the problem is that it is checking the wrong thing to begin with. Why even parse the string, sudo already had the list of files when it constructed the string..
I mean I agree, I'd say it's mostly just an issue of too much separation, they put the argument array together in one piece of code and then pass it to another piece of code that executes it with the necessary permissions. They don't pass along a separate array of files (or the location in the arguments where the files start), so the execute code attempts to figure out where they are instead.
You're correct but sudoedit itself needs to parse the file list to know which files to copy to temporary files as you describe. So in this case you're tricking sudoedit into thinking you want to edit a different file than the one specified originally on the command line.
> Why would one prefer to add sudoedit X to sudoers rather than updating file access privileges of X directly?
Permission complications.
Software may run as user:group, but you don't want to add humans to either, and so you allow them to edit a few files as that user or group from their own account (which also gives you auditing of changes). Some software insists on files (directories) have certain permissions so you're stuff with them.
Or you want a centralized place for permissions, so you put these sudoedit entries in LDAP which can be accessed anywhere in you network, and so you don't have to keep track of individual file permissions on a gazillion systems.
Sudo basically has an ACL-like system where you can specify exactly which users/groups can execute which commands as root. So you can say user foo can execute commands X, Y, and Z as root and user bar can execute commands W, Y, and Z as root, and neither user can use sudo to execute any other command as root. The ACL system isn't for sudoedit specifically, it's a general feature of sudo.
As to why you can't just update access privileges of the file, for most use cases you probably could do that. If you need something more complicated though you'll have to use some terrible ACL implementation like the one in sudo or Posix file ACLs.
I recently got to reading the POSIX.1e (MAC & DAC) draft, and the DAC = ACL part is... surprisingly non-terrible. Still awkward and hampered by its existence as barely-visible metadata smeared over the whole system, as all ACLs are, but not at all the hopeless mess I expected coming from NT. (Even that might’ve been salvageable had Microsoft been willing to publish full documentation of all NT object permissions and mechanisms. Except SDDL, there is no world in which SDDL is salvageable.) Couldn’t make heads or tails of the MAC part, though.
The /etc/sudoers solution does have a usability advantage precisely in not being smeared all over the system. Even if “/etc/sudoers” and “usability” are words not often seen inside a single sentence.
If you as an administrator want to see where you have granted additional funny permissions, with ACLs your only recourse is to getfacl everything on the filesystem, whereas with sudo everything is listed in /etc/sudoers and classically the group membership in /etc/passwd gives you a pretty good idea. I don’t know if that’s a reasonable thing to want, actually, but it is one that makes me mildly unconfortable with ACL systems in general.
In addition to what has been said already, sometimes it's nice to have guard rails, so you're a little more sure that you're only touching that thing when you mean to.
I wonder if this bug in logic (instead of buffer overflows) would also have been less likely in a different language. Would it have been more obvious in a language where it's easier to work with dynamically allocated arrays and strings?
With my Rust hat on: I don't think that Rust would have solved this. It might have made the code in question easier to understand, as you note, but this kind of error can still happen in any language.
Looking at the patch[1], probably not. There isn't really a lot of complex string handling involved; it's basically just forgetting to forbid "--". I don't really see how any language choice could help you with this.
It shouldn't have to forbid that. The editor and privileged files shouldn't be in the same string. It should just be appending the list of temporary files to the editor command, and running that unprivileged.
You don't even need a temporary file; opening the file directly in sudoedit then passing /dev/fd/N to the spawned edit process after dropping privileges would work (a-la capabilities). But sudoedit being implemented in terms of sudo makes it hard.
edit: apparently things are more complex and sudoedit already runs the command unprivileged; the attack is in filename expansion in sudoedit itself.
Besides the fact that that wouldn't actually have prevented this bug (which you acknowledge in your edit), /dev/fd/N is a linuxism, so wouldn't work on other unices. And has slightly different semantics than the current implementation, where your changes to the file aren't actually updated in the original privileged file until after you exit the editor.
I would say a more type-oriented mentality would make this kind of bug less likely; thinking of -- as a magic value rather than a different kind of thing from a regular argument makes it easy to forget the distinction, and a mentality where you're "sanitizing a string" is far less reliable than one where you're transforming between two distinct formats.
Good point. A "parse, don't validate" approach to handling the contents of `$EDITOR` would look like `parseEditorEnv :: String -> Maybe ProgramPath`, and that parse should fail.
But that's the wrong thing. The problem is not that EDITOR must be a program, but (apparently) that they're parsing the expansion of EDITOR (along with other stuff) to figure out what file to operate on.
I don't see a change to language, per se, that would have helped, really.
A system with more of an object capabilities model could have helped, though. The goal wasn't really "let the user run their editor as root (when they ask for it)", but "let the user work with this particular file from their editor (when they ask for it)".
On reflection, I think C is a language where this kind of error is less likely. In languages where it's easy to cram strings together and parse the results, people are more likely to do it. Those things are a bit of a pain in C, so people are more likely to do things another (and in this case better) way. Of course, that was obviously no guarantee.
I don't think that's what's happening. sudoedit does NOT run the editor as root, it copies the file to a temporary as root, runs the editor as you, and copies the temporary over the target file as root when you're done editing it (at least it's supposed to).
I did not mention an editor, so that reading doesn’t follow. Other comments certainly did and could be wrong in the fashion you describe. However, I did not write them, so I feel no need to defend them.
My point is simply the actionable generalisation of your followup, the substantive part of which I don’t even disagree with. In this instance the utility is the wrapper.
Ubuntu shipped the patch three days ago. The output of `apt changelog sudo` on 22.04 LTS:
sudo (1.9.9-1ubuntu2.2) jammy-security; urgency=medium
* SECURITY UPDATE: arbitrary file overwrite via sudoedit
- debian/patches/CVE-2023-22809.patch: do not permit editor arguments
to include -- in plugins/sudoers/editor.c, plugins/sudoers/sudoers.c,
plugins/sudoers/visudo.c.
- CVE-2023-22809
* SECURITY UPDATE: DoS via invalid arithmetic shift in Protobuf-c
- debian/patches/CVE-2022-33070.patch: only shift unsigned values in
lib/protobuf-c/protobuf-c.c.
- CVE-2022-33070
-- Marc Deslauriers <marc.deslauriers@ubuntu.com> Mon, 16 Jan 2023 07:36:33 -0500
It shells out to the EDITOR environment variable, which is controlled by the less privileged user.
In this example they inject running an editor against another file.
I'm guessing you can put arbitrary code in there or point it at a locally controlled executable too. But I'm not sure. Maybe sudoedit puts more scrutiny on that variable than most, non-security programs. At any rate many text editors have lots of modules and scripting and can presumably load and execute code as the privileged user.
The workaround is to change the sudo config file to remove the EDITOR environment variable and a few others.
* https://security-tracker.debian.org/tracker/CVE-2023-22809
* https://ubuntu.com/security/CVE-2023-22809
* https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2023-22809
Debian has links to the others.