Coordinated Disclosure Timeline

Summary

There is an authentication bypass vulnerability in polkit, which enables an unprivileged user to get authorization from polkit to perform a privileged action.

Product

polkit

Tested Versions

Details

Issue 1: Authentication bypass in polkit (GHSL-2021-074)

The function polkit_system_bus_name_get_creds_sync is used to get the uid and pid of the process requesting the action. It does this by sending the unique bus name of the requesting process, which is typically something like “:1.96”, to dbus-daemon. These unique names are assigned and managed by dbus-daemon and cannot be forged, so this is a good way to check the privileges of the requesting process.

The vulnerability happens when the requesting process disconnects from dbus-daemon just before the call to polkit_system_bus_name_get_creds_sync starts. In this scenario, the unique bus name is no longer valid, so dbus-daemon sends back an error reply. This error case is handled in polkit_system_bus_name_get_creds_sync by setting the value of the error parameter, but it still returns TRUE, rather than FALSE. We are not sure whether it’s a bug that the return value is TRUE when this error happens, but this behavior certainly means that all callers of polkit_system_bus_name_get_creds_sync need to carefully check whether an error was set. If the calling function forgets to check for errors then it will think that the uid of the requesting process is 0 (because the AsyncGetBusNameCredsData struct is zero initialized). In other words, it will think that the action was requested by a root process, and will therefore allow it.

Most of the callers of polkit_system_bus_name_get_creds_sync check the error value correctly, and are therefore not vulnerable. But the error value is not checked in the following stack trace:

0  in polkit_system_bus_name_get_creds_sync of polkitsystembusname.c:393
1  in polkit_system_bus_name_get_user_sync of polkitsystembusname.c:511
2  in polkit_backend_session_monitor_get_user_for_subject of polkitbackendsessionmonitor-systemd.c:303
3  in check_authorization_sync of polkitbackendinteractiveauthority.c:1113
4  in check_authorization_sync of polkitbackendinteractiveauthority.c:1223
5  in polkit_backend_interactive_authority_check_authorization of polkitbackendinteractiveauthority.c:971
6  in server_handle_check_authorization of polkitbackendauthority.c:795
7  in server_handle_method_call of polkitbackendauthority.c:1274

The bug is in this snippet of code in check_authorization_sync:

/* every subject has a user; this is supplied by the client, so we rely
 * on the caller to validate its acceptability. */
user_of_subject = polkit_backend_session_monitor_get_user_for_subject (priv->session_monitor,
                                                                       subject, NULL,
                                                                       error);
if (user_of_subject == NULL)
    goto out;

/* special case: uid 0, root, is _always_ authorized for anything */
if (POLKIT_IS_UNIX_USER (user_of_subject) && polkit_unix_user_get_uid (POLKIT_UNIX_USER (user_of_subject)) == 0)
  {
    result = polkit_authorization_result_new (TRUE, FALSE, NULL);
    goto out;
  }

Notice that the value of error is not checked.

Impact

This issue may lead to local privilege escalation on any Linux system that uses polkit.

Resources

Proof of concept exploit: GHSL-2021-074-polkit.bundle

CVE

Credit

This issue was discovered and reported by GHSL team member @kevinbackhouse (Kevin Backhouse).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2021-074 in any communication regarding this issue.