Hacker News new | past | comments | ask | show | jobs | submit login
Pwnkit: Local Privilege Escalation in polkit's pkexec (qualys.com)
181 points by pajtai on Jan 26, 2022 | hide | past | favorite | 41 comments



Here's a shorter summary of the vulnerability:

    for (n = 1; n < (guint) argc; n++)
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.


It is a truly excellent vulnerability that hits a weird POSIX corner case, coupled with a fairly subtle coding error.


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.


The piece of polkit that this exploits was blogged about in 2013: https://ryiron.wordpress.com/2013/12/16/argv-silliness/


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?"


If this was known in 2013 why wasn't it fixed? This is literally a simple error in parsing program arguments.


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):

https://gist.github.com/pothos/73dd4f7694acc3b6bbed614438f6e...

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.


Doesn't --scope run the process as a child? Is this done without setuid?


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.


I said with --scope:

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


There's irony in the man page telling you not to despair. There's an xkcd about it: https://xkcd.com/1343/

By comparison, the manual to doas.conf is pretty short: https://man.openbsd.org/doas.conf.5

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

More: https://marc.info/?l=openbsd-ports&m=143465998814989&w=2




From Wikipedia for Polkit

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


> Does a lot of software depend on pkexec?

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.


On my desktop, only pipewire and intel video drivers depend on polkit. I don't know if they actually use pkexec though.


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/


Fine-grained sandboxing. Memory-safe languages. Safer syscalls (e.g. returning EFAULT when calling "execve(pointer to null array)").

There are lots of ways to improve the current state of security.


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


What are your suggestions or projects for improving the state of Linux and Unix security?


Let's take this basic example:

  # ll /usr/bin/passwd
  -rwsr-xr-x. 1 root root 36760 Feb 16  2020 /usr/bin/passwd
The setuid permission on that program allows it to escalate to root for part of it's runtime.

Why are we doing this?

I could make a new "passwd" user, and assign it a non-zero UID and GID, setuid to that user instead, making it own this file:

  # ll /etc/passwd
  -rw-r--r--. 1 root root 3346 Apr  8  2021 /etc/passwd
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.


I kinda like this idea, although I'm unlikely to implement it (at least in the near future). A question though.

> root users should not be held in either of their databases

Where would you propose keeping the information for UID 0?


How about:

  /etc/passed.root
  /etc/shadow.root
  /etc/group.root
I don't know if the handling of the "wheel" group should be any different. It's still enforced on OpenBSD for su.


Um, if you can compromise "passwd" it's game over; you are root equivalent.

Looking at other suids in /bin...

Compromise mount, game over.

Compromise su, game over.

arping? ping? Dunno; they can generating normally-prohibited packets.

umount? Hmmm.... feels like some kind of clever trick lurking here.

As for kernel subsystems, there's very little which cannot lead to full system compromise, either directly or indirectly by tricking a user.


Your loaded premise is that the "tooling, verification, testing" would catch this. Can you illustrate how?


There is already POC code published: https://github.com/mebeim/CVE-2021-4034


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.

[0] https://github.com/PeterGottesman/pwnkit-exploit


If polkit is "sudo for GUIs", are there any use cases for polkit on a non-GUI Linux server?


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.

/usr/bin/chfn

/usr/bin/chage

/usr/bin/chsh

/usr/bin/sudo

/usr/bin/crontab

/usr/bin/su

/usr/bin/ssh-agent

/usr/bin/newgrp

/usr/bin/passwd




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

Search: