Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2021-3560 — C exploit for CVE-2021-3560, an authentication bypass in polkit enabling unprivileged users to create a privileged account via DBus, with a detailed technical writeup. | Kitploit
Tools/GitHubGitHub/hakivvi/cve-2021-3560
Authentication & AuthorizationPrivilege EscalationVulnerability AnalysisExploitationPenetration TestingRed Teaming
GitHubhakivvi/cve-2021-3560

CVE-2021-3560

C exploit for CVE-2021-3560, an authentication bypass in polkit enabling unprivileged users to create a privileged account via DBus, with a detailed technical writeup.

View Repository
4012105 years agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2021-3560

a reliable C based exploit for CVE-2021-3560.

Summary:

Yestreday i stumbled upon this blog post by Kevin Backhouse (discovered this vulnerability), i tried the bash commands provided in the blogpost and to my surpise it worked on my Kali Linux box!

CVE-2021-3560 is an authentication bypass on polkit, which allows an unprivileged user to call privileged methods using DBus, the PoC exploits this bug to call 2 privileged methods provided by accountsservice (CreateUser and SetPassword), which allows us to create a priviliged user then setting a password to it.

polkit checks if the caller is authorized to call such method, it does so by checking first the user id of the caller, if it is zero then the caller is assumed to be root and the action is allowed without asking for authentication, otherwise it asks for the password of the user.

polkit_system_bus_name_get_creds_sync() function invokes to 2 methods to get the UID and PID of the caller GetConnectionUnixUser and GetConnectionUnixProcessID, the result of this calls is written to the data struct of type AsyncGetBusNameCredsData (this struct is intialized to 0) by the callback function on_retrieved_unix_uid_pid(), and polkit_system_bus_name_get_creds_sync() blocks while waiting for the callback function to set an error or the UID and PID.

static gboolean
polkit_system_bus_name_get_creds_sync (PolkitSystemBusName           *system_bus_name,
				       guint32                       *out_uid,
				       guint32                       *out_pid,
				       GCancellable                  *cancellable,
				       GError                       **error)
{
  gboolean ret = FALSE;
  AsyncGetBusNameCredsData data = { 0, }; // intialize to 0
  GDBusConnection *connection = NULL;
  GMainContext *tmp_context = NULL;

  connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
  if (connection == NULL)
    goto out;

  data.error = error;

  tmp_context = g_main_context_new ();
  g_main_context_push_thread_default (tmp_context);

  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixUser",      /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid, // callback funtion
			  &data); // data is passed to the callback function along with the reply from the method
  g_dbus_connection_call (connection,
			  "org.freedesktop.DBus",       /* name */
			  "/org/freedesktop/DBus",      /* object path */
			  "org.freedesktop.DBus",       /* interface name */
			  "GetConnectionUnixProcessID", /* method */
			  g_variant_new ("(s)", system_bus_name->name),
			  G_VARIANT_TYPE ("(u)"),
			  G_DBUS_CALL_FLAGS_NONE,
			  -1,
			  cancellable,
			  on_retrieved_unix_uid_pid, // callback funtion
			  &data); // data is passed to the callback function along with the reply from the method

  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // block while on_retrieved_unix_uid_pid() is not called yet
    g_main_context_iteration (tmp_context, TRUE);

the callback function on_retrieved_unix_uid_pid() is invoked after each method call to retirive the reply (UID and PID) or set an error, this function calls g_dbus_connection_call_finish() to retrive the reply if an error is occured then it sets data.caught_error to TRUE and returns (data.uid and data.pid are still set 0). otherwise it assign the retrieved value (UID or PID) to data.uid or data.pid (depends on what value is retrieved) then returns.

static void
on_retrieved_unix_uid_pid (GObject              *src, // connection
			   GAsyncResult         *res, // Async result object
			   gpointer              user_data) // data paramter passed from previous function
{
  AsyncGetBusNameCredsData *data = user_data;
  GVariant *v;

  v = g_dbus_connection_call_finish ((GDBusConnection*)src, res,
				     data->caught_error ? NULL : data->error); // finish and get the reply
  if (!v) // error ??
    {
      data->caught_error = TRUE;
    }
  else
    {
      guint32 value;
      g_variant_get (v, "(u)", &value); // unpack the reply, get UINT32 (u)
      g_variant_unref (v);
      if (!data->retrieved_uid) // GetConnectionUnixUser method
	{
	  data->retrieved_uid = TRUE;
	  data->uid = value;
	}
      else
	{
	  g_assert (!data->retrieved_pid); // GetConnectionUnixProcessID method
	  data->retrieved_pid = TRUE;
	  data->pid = value;
	}
    }
}

GetConnectionUnixUser and GetConnectionUnixProcessID methods will return the UID and PID if found (caller process is still connected to the bus), or an error if an error occured (e.g: caller process killed).

once data.uid and data.pid are set or data.caught_error is set the function polkit_system_bus_name_get_creds_sync() will continue and here where the vulnerabilty exists, polkit_system_bus_name_get_creds_sync() does not return an error if data.caught_error is set, instead it set whatever value is in data.uid to out_uid and returns TRUE (even if data.caught_error is set). out_pid is a pointer to a guint32 variable passed to polkit_system_bus_name_get_creds_sync() when called by polkit_system_bus_name_get_user_sync():

static gboolean
polkit_system_bus_name_get_creds_sync (PolkitSystemBusName           *system_bus_name,
				       guint32                       *out_uid, // pointer
				       guint32                       *out_pid, // NULL
				       GCancellable                  *cancellable,
				       GError                       **error)
{

  [snip]
  
  while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error)) // wait for the callback function to handle reply
    g_main_context_iteration (tmp_context, TRUE);

  if (out_uid) // TRUE
    *out_uid = data.uid; // set it even if there is an error [!]
  if (out_pid) // FALSE
    *out_pid = data.pid; // set it even if there is an error [!]
  ret = TRUE; // return TRUE even if there is an error [!]
 out:
  if (tmp_context)
    {
      g_main_context_pop_thread_default (tmp_context);
      g_main_context_unref (tmp_context);
    }
  if (connection != NULL)
    g_object_unref (connection);
  return ret;

exploitation:

if the a process A calls a priviliged method using DBus, then polkit will check the UID of the caller, if process A exits imidiatly after it sends the message, then the methods GetConnectionUnixUser and GetConnectionUnixProcessID will return an error because the caller process does not exist anymore. the callback function on_retrieved_unix_uid_pid() will set data.caught_error to TRUE, data.uid and data.pid will remain unchanged (which means they will be both set to 0 as the data struct is intiailized to 0). the function polkit_system_bus_name_get_creds_sync() will continue the execution and sets out_uid to data.uid (0), and return TRUE.

Download Tool