Usually, after that loop, n equals argc (or could be less than argc if the loop breaks or something). But if argc is 0, then n will equal 1 after. Later on, the program writes to argv[n]. If argc is 0, then argv[n] is argv[1], which is out-of-bounds, but happens to refer to the same place as envp[0] (at least on Linux). This lets the attacker set an arbitrary environment variable, after ld.so(8) already sanitized the environment. In particular, setting the GCONV_PATH environment variable (which is one that would have been removed during sanitization) will cause pkexec to load and execute code from a .so file in an attacker-controlled directory.
How does this exploit avoid environment sanitization? Is that because most environment sanitization (e.g. clearenv) adjusts the pointer rather than the memory, and argv[argc + 1] is the original environment location?
It's because this environment manipulation occurs after ld.so sanitized the environment, and there are opportunities to then trigger behavior based on the environment before pkexec re-sanitizes it. The GCONV_PATH variable in particular makes it load shared code when attempting to log an error.
Because the code that gets exploited to set the environment variable runs after sanitization has already finished, and no further sanitization happens between then and when it gets read.
Great find...so they gave up at "However, because we can’t pass any option arguments, the executing user will default to root, which presumably we don’t have the password for."
It seems possible the current exploit authors read this and picked up from there..."what else could we do given that we can't pass options?"
This is the dark side of risk based security which often manifests. The code gets audited over a day or two and items get ranked by severity. The “minor” bugs are fixed much later if never and it’s ok because they are low severity. But if you pull on the threads of minor issues there are usually deeper issues at play. Further bugs might be minor if considered independently, but can be chained together to perform severe exploits. Given the resources, I think the best approach is to just aggressively eliminate all known undefined behavior in a application
Setuid programs like pkexec or sudo are hard to get right… Here an example of using systemd-run (not setuid, and the wrapper script neither) for something similar like sudo (but it uses a systemd unit instead of a direct child process in the same process group):
Since polkit is used (but not the setuid pkexec, this was the point of this exercise) there is still one setuid binary invoked for the PAM check, though.
It won't be a child process because PID 1 will actually start this unit. Yes, without setuid because PID 1 takes the request from the unprivileged systemd-run client.
> If a command is run as transient scope unit, it will be executed by systemd-run itself as parent process and will thus inherit the execution environment of the caller. (...) This mode is enabled via the --scope switch
Sudo got so complex that OpenBSD replaced it with their own much simpler "doas." But even doas has grown some additional features since it was first released.
What's the origin of this complexity? Too many configurations and settings? The project being really old and lacking a strong culture around software architecture? Something else?
I believe it's mostly years of "creeping featuritis" but I don't really know the history in detail. Just read the sudoers(5) manpage and let the sense of hopelessness slowly overtake you.
It seems like OpenBSD was also stuck on an older version of sudo when it was removed. It's possible they just weren't having all the churn of such a large tool? But then they also missed a bunch of important fixes: https://marc.info/?l=openbsd-bugs&m=142270265216628&w=2
> It is also possible to use polkit to execute commands with elevated privileges using the command pkexec followed by the command intended to be executed (with root permission).[7] However, it may be preferable to use sudo, as this command provides more flexibility and security, in addition to being easier to configure.[8]
> A memory corruption vulnerability PwnKit (CVE-2021-4034[10]) discovered in the pkexec command (installed on all major Linux distributions) was announced on January 25, 2022.[11][12] The vulnerability dates back to the original distribution from 2009. The vulnerability received a CVSS score of 7.8 ("High severity") reflecting serious factors involved in a possible exploit: unprivileged users can obtain full root privileges, independent of the underlying machine architecture, regardless of whether the polkit daemon is running or not.
Does a lot of software depend on pkexec? How could it be secured to be recommended over sudo?
If I try to remove polkit here, it would result in the loss of various parts of KDE and Gnome, PCManFM, and virt-manager. I'm not sure if it's a hard dependency, a dependency of a dependency, or what. I'm also not sure if they use pkexec specifically.
This is a great write-up, and also a great illustration of something that's fundamentally broken in the way Linux and Unix think about security.
`suid` binaries exist for a good reason: they allow users to escalate their privilege in limited ways under control of the system. Good, but obviously risky if the user can subvert that control. The offered solution to that risk is where the problem comes in. Developers of suid binaries are just asked to write them really, really carefully. There are some documents to describe how to do it carefully, but they pretty much assume that code can be developed without bugs, as long as we're careful enough. At least initially, there was no help, no tooling, no verification, no testing, nothing. Just real, real careful.
Unfortunately, instead of seeing the absurdity of that situation, many people have gone all "no true Scotsman" on it. "If you were a real Unix Developer then you wouldn't make mistakes like that." We don't need no stinking tools, we're Real Programmers.
The situation has been improving, but the underlying mechanism is still super popular and here to stay. It's either time to stop pretending that local `root` means anything, or start taking stuff like this much much more seriously. Some people sure are, but it hasn't pervaded the culture yet.
Ironically, polkit is designed to avoid the need to have suid binaries, since it allows other programs to obtain restricted privileges without full suid. The problem is, polkit itself still needs suid to hand out privileges to other programs.
Yes and no, applications integrated with Polkit need to have privileges (usually by running as root). Polkit itself doesn't need any privileges since it's just a framework for programs that have elevated privileges to ask if a user is allowed to use them.
pkexec a program bundled with but not really part of Polkit is suid as the method it uses to gain elevated privileges rather than running a daemon.
I don't really understand your point. The kernel, cryptographic primitives, and device drivers also need to be developed really, really carefully. Some things require more care than others. What would be the alternative?
It seems like a good first goal would be to dramatically reduce the size of the code base you need to trust (especially complex code with high privilege), and focus on verifying the correctness of that code base. Then, have fine-grained encapsulation mechanisms for things that do need some level of privilege, and especially avoid conflating "can administer the system" and "can access all user data" levels of privilege.
As an example, although one that practically seems to have turned out harder to do than the authors expected, take a look at HiStar: http://www.scs.stanford.edu/histar/
> Unfortunately, instead of seeing the absurdity of that situation, many people have gone all "no true Scotsman" on it. "If you were a real Unix Developer then you wouldn't make mistakes like that." We don't need no stinking tools, we're Real Programmers.
That is the problem. Unix, and the 'philosophy'.
I used to like it, but it is over half a century old and as well as everything 'unix-like' it was not designed for security, especially the C programming language. Thus, with everything 'unix-like', they have imported all the mistakes with it. The same with the legacy like X11, allowing root to modify system files, etc.
Unix is dead. Might need to move on from such prehistoric systems to ones that are designed for security that are not 'Unix'.
Isn't it more of a slippery slope? I want to be able run whatever I want on my system. If I get malicious code, who's fault is it? Maybe my browsing habits, the tasks I want done, the time I can spend on roling my own, the documentation (haha, what documentation), the schools, or the government and AT&T? Who is this real Unix Developer? The Linux Kernel has a stelar track record, just the towers of complexity built around it are growing like the weeds. The kernel isn't making it easy, hardware developers don't play nice, and every one wannabe expert wants it to be just working.
There's the actual "No True Scotsman", because capability based kernels exist for a long time now. Yet, users want something to work with, actual software that needs to be written, that doesn't even have to be in C.
The expressed sentiment is entirely independent of the domain. You simply need to know what you are doing or things won't move forward. It's only consequential that those whom rely on Unixoid systems for whichever reason do express this sentiment in terms of those systems.
The alternative are walled gardens, where criticizm is impossible. Because with secure boot and the like it would require breaking the system to take control.
Ironically, Multics did continue without Bell Labs and not surprisingly DoD security assessment for it was higher than UNIX with praise for their use of PL/I in preventing memory corruption bugs.
But it wasn't available as free tapes for universities to play with, so....
UNIX in general seems to have a root fixation on setuid. Both passwd and shadow users should exist, with separate roles and privileges, and root users should not be held in either of their databases.
Worth noting that this and the other exploit that was posted on twitter last night do not work (at least) on Fedora 34, and some versions of OpenSUSE. Generally they will not work on versions of polkit which disable GVFS. My POC [0] handles this by just setting the relevant env variable explicitly.
There's zero reason for policykit to be installed on multiuser systems or servers. This is just a part of bizarre obsession with "new tools" bullshit coming from RedHat and the inept software developers it accumulates.
The following should be the output of "find / -type f -perm -4000 -o -perm -2000" on a VM. If it is a container, you might need an "su" and/or "sudo" and maybe, if for some reason you are using a cron internal to the container (but why?!), crontab.