Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
nu11secur1ty
GitHub Repository: nu11secur1ty/Kali-Linux
Path: blob/master/BlueKeep/cve_2019_0708_bluekeep_rce/rdp.rb
1306 views
1
# -*- coding: binary -*-
2
3
module Msf
4
5
###
6
#
7
# This module exposes methods for interacting with a remote RDP service
8
#
9
###
10
module Exploit::Remote::RDP
11
require 'rc4'
12
include Msf::Exploit::Remote::Tcp
13
14
#
15
# Creates an instance of a RDP exploit module.
16
#
17
def initialize(info = {})
18
super
19
register_options(
20
[
21
OptString.new('RDP_USER', [ false, 'The username to report during connect, UNSET = random']),
22
OptString.new('RDP_CLIENT_NAME', [ false, 'The client computer name to report during connect, UNSET = random', 'rdesktop']),
23
OptString.new('RDP_DOMAIN', [ false, 'The client domain name to report during connect']),
24
OptAddress.new('RDP_CLIENT_IP', [ true, 'The client IPv4 address to report during connect', '192.168.0.100']),
25
Opt::RPORT(3389)
26
], Msf::Exploit::Remote::RDP)
27
end
28
29
30
# used to abruptly abort scanner for a given host
31
class RdpCommunicationError < StandardError
32
end
33
34
#
35
# Constants
36
#
37
class RDPConstants
38
SSL_REQUIRED_BY_SERVER = 1
39
SSL_NOT_ALLOWED_BY_SERVER = 2
40
SSL_CERT_NOT_ON_SERVER = 3
41
INCONSISTENT_FLAGS = 4
42
HYBRID_REQUIRED_BY_SERVER = 5
43
SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 6
44
45
PROTOCOL_RDP = 0
46
PROTOCOL_SSL = 1
47
PROTOCOL_HYBRID = 2
48
PROTOCOL_RDSTLS = 4
49
PROTOCOL_HYBRID_EX = 8
50
51
RDP_NEG_PROTOCOL = {
52
0 => "PROTOCOL_RDP",
53
1 => "PROTOCOL_SSL",
54
2 => "PROTOCOL_HYBRID",
55
4 => "PROTOCOL_RDSTLS",
56
8 => "PROTOCOL_HYBRID_EX"
57
}
58
59
RDP_NEG_FAILURE = {
60
1 => "SSL_REQUIRED_BY_SERVER",
61
2 => "SSL_NOT_ALLOWED_BY_SERVER",
62
3 => "SSL_CERT_NOT_ON_SERVER",
63
4 => "INCONSISTENT_FLAGS",
64
5 => "HYBRID_REQUIRED_BY_SERVER",
65
6 => "SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER"
66
}
67
68
REDIRECTION_SUPPORTED = 0x1
69
REDIRECTION_VERSION3 = 0x2 << 2
70
REDIRECTION_VERSION4 = 0x3 << 2
71
REDIRECTION_VERSION5 = 0x4 << 2
72
73
ENCRYPTION_40BIT = 0x01
74
ENCRYPTION_128BIT = 0x02
75
ENCRYPTION_56BIT = 0x08
76
ENCRYPTION_FIPS = 0x10
77
78
CHAN_INITIALIZED = 0x80000000
79
CHAN_ENCRYPT_RDP = 0x40000000
80
CHAN_ENCRYPT_SC = 0x20000000
81
CHAN_ENCRYPT_CS = 0x10000000
82
CHAN_PRI_HIGH = 0x08000000
83
CHAN_PRI_MED = 0x04000000
84
CHAN_PRI_LOW = 0x02000000
85
CHAN_COMPRESS_RDP = 0x00800000
86
CHAN_COMPRESS = 0x00400000
87
CHAN_SHOW_PROTOCOL = 0x00200000
88
CHAN_REMOTE_CONTROL_PERSISTENT = 0x00100000
89
90
CHAN_FLAG_FIRST = 0x01
91
CHAN_FLAG_LAST = 0x02
92
CHAN_FLAG_SHOW_PROTOCOL = 0x10
93
94
RDPDR_CTYP_CORE = 0x4472
95
96
PAKID_CORE_SERVER_ANNOUNCE = 0x496e
97
PAKID_CORE_SERVER_CAPABILITY = 0x5350
98
PAKID_CORE_CLIENTID_CONFIRM = 0x4343
99
PAKID_CORE_CLIENT_NAME = 0x434e
100
PAKID_CORE_DEVICELIST_ANNOUNCE = 0x4441
101
end
102
103
def rdp_connect
104
self.rdp_sock = connect(false)
105
self.rdp_sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
106
end
107
108
109
def rdp_disconnect
110
disconnect(self.rdp_sock)
111
self.rdp_sock = nil
112
end
113
114
def rdp_send(data)
115
self.rdp_sock.put(data)
116
end
117
118
def rdp_recv(length = -1, timeout = 5)
119
res = self.rdp_sock.get_once(length, timeout)
120
raise RdpCommunicationError unless res # nil due to a timeout
121
122
res
123
rescue EOFError
124
raise RdpCommunicationError
125
end
126
127
def rdp_send_recv(data)
128
rdp_send(data)
129
rdp_recv
130
end
131
132
# Connect and perform fingerprinting of the RDP service
133
#
134
# Note: NLA is required to detect the product_version
135
#
136
# @return [Boolean] Is service RDP
137
# @return [Hash] Version information
138
def rdp_fingerprint
139
peer_info = {}
140
# warning: if rdp_check_protocol starts handling NLA, this will need to be updated
141
is_rdp, server_selected_proto = rdp_check_protocol(RDPConstants::PROTOCOL_SSL | RDPConstants::PROTOCOL_HYBRID | RDPConstants::PROTOCOL_HYBRID_EX)
142
return false, nil unless is_rdp
143
return true, peer_info unless [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include? server_selected_proto
144
145
swap_sock_plain_to_ssl
146
ntlm_negotiate_blob = '' # see: https://fadedlab.wordpress.com/2019/06/13/using-nmap-to-extract-windows-info-from-rdp/
147
ntlm_negotiate_blob << "\x30\x37\xa0\x03\x02\x01\x60\xa1\x30\x30\x2e\x30\x2c\xa0\x2a\x04\x28"
148
ntlm_negotiate_blob << "\x4e\x54\x4c\x4d\x53\x53\x50\x00" # Identifier - NTLMSSP
149
ntlm_negotiate_blob << "\x01\x00\x00\x00" # Type: NTLMSSP Negotiate - 01
150
ntlm_negotiate_blob << "\xb7\x82\x08\xe2" # Flags (NEGOTIATE_SIGN_ALWAYS | NEGOTIATE_NTLM | NEGOTIATE_SIGN | REQUEST_TARGET | NEGOTIATE_UNICODE)
151
ntlm_negotiate_blob << "\x00\x00" # DomainNameLen
152
ntlm_negotiate_blob << "\x00\x00" # DomainNameMaxLen
153
ntlm_negotiate_blob << "\x00\x00\x00\x00" # DomainNameBufferOffset
154
ntlm_negotiate_blob << "\x00\x00" # WorkstationLen
155
ntlm_negotiate_blob << "\x00\x00" # WorkstationMaxLen
156
ntlm_negotiate_blob << "\x00\x00\x00\x00" # WorkstationBufferOffset
157
ntlm_negotiate_blob << "\x0a" # ProductMajorVersion = 10
158
ntlm_negotiate_blob << "\x00" # ProductMinorVersion = 0
159
ntlm_negotiate_blob << "\x63\x45" # ProductBuild = 0x4563 = 17763
160
ntlm_negotiate_blob << "\x00\x00\x00" # Reserved
161
ntlm_negotiate_blob << "\x0f" # NTLMRevision = 5 = NTLMSSP_REVISION_W2K3
162
resp = rdp_send_recv(ntlm_negotiate_blob)
163
164
ntlmssp_start = resp.index('NTLMSSP')
165
if ntlmssp_start
166
ntlmssp = NTLM_MESSAGE::parse(resp[ntlmssp_start..-1])
167
version = ntlmssp.padding.bytes
168
peer_info[:product_version] = "#{version[0]}.#{version[1]}.#{version[2] | (version[3] << 8)}"
169
end
170
171
return is_rdp, peer_info
172
end
173
174
def rdp_dispatch_loop
175
while rdp_sock do
176
rdp_handle_packet(rdp_recv)
177
end
178
end
179
180
def rdp_create_channel_msg(chan_user_id, chan_id, data, flags = 3, data_length = nil)
181
data_length ||= data.length
182
183
pdu = [
184
[25 << 2].pack('C'), # MCS send data request structure, choice 25
185
[self.rdp_user_id, chan_id].pack('S>S>'), # MCS send data request structure, choice 25
186
"\x70", # Wut (security header)
187
per_data(
188
[data_length].pack('<L'),
189
[flags].pack('<L'),
190
data
191
)
192
].join('')
193
194
build_data_tpdu(pdu)
195
end
196
197
def rdp_send_channel(chan_user_id, chan_id, data, flags = 3, data_length = nil)
198
tpkt = rdp_create_channel_msg(chan_user_id, chan_id, data, flags, data_length)
199
rdp_send(tpkt)
200
end
201
202
def rdp_terminate
203
body = "\x21\x80" # user requested disconnect provider ultimatum
204
205
rdp_send(build_data_tpdu(body))
206
end
207
208
# Connect and detect security protocol
209
#
210
# Note: NLA is detected but not supported yet
211
#
212
# @return [Boolean] Is service RDP
213
# @return [RDPConstants] Protocol supported
214
def rdp_check_protocol(req_proto = RDPConstants::PROTOCOL_SSL)
215
if datastore['RDP_USER']
216
@user_name = datastore['RDP_USER']
217
else
218
@user_name = Rex::Text.rand_text_alpha(7)
219
end
220
221
if datastore['RDP_DOMAIN']
222
@domain = datastore['RDP_DOMAIN']
223
else
224
@domain = Rex::Text.rand_text_alpha(7)
225
end
226
227
if datastore['RDP_CLIENT_NAME']
228
@computer_name = datastore['RDP_CLIENT_NAME']
229
else
230
@computer_name = Rex::Text.rand_text_alpha(15)
231
end
232
233
@ip_address = datastore['RDP_CLIENT_IP']
234
235
# code to check if RDP is open or not
236
vprint_status("Verifying RDP protocol...")
237
238
vprint_status("Attempting to connect using TLS security")
239
res = rdp_send_recv(pdu_negotiation_request(@user_name, req_proto))
240
241
# return true if the response is a X.224 Connect Confirm
242
# We can't use a check for RDP Negotiation Response because WinXP excludes it
243
if res
244
result, err_msg = rdp_parse_negotiation_response(res)
245
return true, result if result
246
247
# No current support for NLA, nothing to do here
248
return true, RDPConstants::PROTOCOL_HYBRID if err_msg == 'HYBRID_REQUIRED_BY_SERVER'
249
250
if err_msg == "Negotiation Response packet too short."
251
vprint_status("Attempt to connect with TLS failed but looks like the target is Windows XP")
252
else
253
vprint_status("Attempt to connect with TLS failed with error: #{err_msg}")
254
end
255
256
if ["SSL_NOT_ALLOWED_BY_SERVER", "Negotiation Response packet too short."].include? err_msg
257
# This happens if the server is configured to ONLY permit RDP Security
258
vprint_status("Attempting to connect using Standard RDP security")
259
rdp_disconnect
260
rdp_connect
261
res = rdp_send_recv(pdu_negotiation_request(@user_name, RDPConstants::PROTOCOL_RDP))
262
263
if res
264
result, err_msg = rdp_parse_negotiation_response(res)
265
return true, result if result
266
267
# Windows XP doesn't return the standard Negotiation Response packet
268
# but we at least know this was RDP since the packet contained a
269
# Connect-Confirm response (0xd0).
270
if err_msg == "Negotiation Response packet too short."
271
return true, RDPConstants::PROTOCOL_RDP
272
end
273
274
vprint_status("Attempt to connect with Standard RDP failed with error #{err_msg}")
275
end
276
end
277
end
278
279
return false, 0
280
end
281
282
# Negotiate security protocol and begin session building
283
#
284
# @return [Boolean] success
285
def rdp_negotiate_security(channels, req_proto = RDPConstants::PROTOCOL_SSL)
286
if req_proto == RDPConstants::PROTOCOL_SSL
287
swap_sock_plain_to_ssl
288
res = rdp_send_recv(pdu_connect_initial(channels, req_proto, @computer_name))
289
elsif req_proto == RDPConstants::PROTOCOL_RDP
290
res = rdp_send_recv(pdu_connect_initial(channels, req_proto, @computer_name))
291
rsmod, rsexp, _rsran, server_rand, bitlen = rdp_parse_connect_response(res)
292
elsif [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include?(req_proto)
293
vprint_status("NLA Security protocol unsupported at this time.")
294
return false
295
else
296
vprint_error("Unknown protocol requested (#{req_proto}).")
297
return false
298
end
299
300
# erect domain and attach user
301
vprint_status("Sending erect domain request")
302
rdp_send(pdu_erect_domain_request)
303
res = rdp_send_recv(pdu_attach_user_request)
304
305
self.rdp_user_id = res[9, 2].unpack("n").first
306
307
# send channel requests
308
[1009, 1003, 1004, 1005, 1006, 1007, 1008].each do |chan|
309
rdp_send_recv(pdu_channel_join_request(self.rdp_user_id, chan))
310
end
311
312
if req_proto == RDPConstants::PROTOCOL_RDP
313
@rdp_sec = true
314
315
# 5.3.4 Client Random Value
316
client_rand = ''
317
32.times { client_rand << rand(0..255) }
318
rcran = bytes_to_bignum(client_rand)
319
320
vprint_status("Sending security exchange PDU")
321
rdp_send(pdu_security_exchange(rcran, rsexp, rsmod, bitlen))
322
323
# We aren't decrypting anything at this point. Leave the variables here
324
# to make it easier to understand in the future.
325
rc4encstart, _rc4decstart, @hmackey, _sessblob = rdp_calculate_rc4_keys(client_rand, server_rand)
326
327
@rc4enckey = RC4.new(rc4encstart)
328
end
329
330
return true
331
end
332
333
# Finish building session after all security is negotiated
334
def rdp_establish_session
335
vprint_status("Sending client info PDU")
336
res = rdp_send_recv(rdp_build_pkt(pdu_client_info(@user_name, @domain, @ip_address), "\x03\xeb", true))
337
vprint_status("Received License packet")
338
339
# Windows XP sometimes sends a very large license packet. This is likely
340
# some form of license error. When it does this it doesn't send a Server
341
# Demand packet. If we wait on one we will time out here and error. We
342
# can still successfully check for vulnerability anyway.
343
if res.length <= 34
344
vprint_status("Waiting for Server Demand packet")
345
_res = rdp_recv
346
vprint_status("Received Server Demand packet")
347
end
348
349
vprint_status("Sending client confirm active PDU")
350
rdp_send(rdp_build_pkt(pdu_client_confirm_active))
351
352
vprint_status("Sending client synchronize PDU")
353
vprint_status("Sending client control cooperate PDU")
354
# Unsure why we're using 1009 here but it works.
355
synch = rdp_build_pkt(pdu_client_synchronize(1009))
356
coop = rdp_build_pkt(pdu_client_control_cooperate)
357
rdp_send(synch + coop)
358
359
vprint_status("Sending client control request control PDU")
360
rdp_send(rdp_build_pkt(pdu_client_control_request))
361
362
vprint_status("Sending client input sychronize PDU")
363
rdp_send(rdp_build_pkt(pdu_client_input_event_sychronize))
364
365
vprint_status("Sending client font list PDU")
366
rdp_send(rdp_build_pkt(pdu_client_font_list))
367
end
368
369
#
370
# Protocol parsers
371
#
372
373
# Parse RDP Negotiation Data - 2.2.1.2
374
# Reference: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/13757f8f-66db-4273-9d2c-385c33b1e483
375
# @return [String, nil] String representation of the Selected Protocol or nil on failure
376
# @return [String] Error message
377
def rdp_parse_negotiation_response(data)
378
return false, "Response is not an RDP Negotiation Response packet." unless data.match("\x03\x00\x00..\xd0")
379
return false, "Negotiation Response packet too short." if data.length < 19
380
381
response_code = data[11].unpack("C")[0]
382
383
if response_code == 2 # TYPE_RDP_NEG_RSP
384
# RDP Negotiation Response - 2.2.1.2.1
385
server_selected_proto = data[15..18].unpack("L<")[0]
386
387
proto_label = RDPConstants::RDP_NEG_PROTOCOL[server_selected_proto]
388
return server_selected_proto, nil if proto_label
389
390
return nil, "Unknown protocol in Negotiation Response: #{server_selected_proto}"
391
392
elsif response_code == 3 # TYPE_RDP_NEG_FAILURE
393
# RDP Negotiation Failure - 2.2.1.2.2
394
failure_code = data[15..18].unpack("L<")[0]
395
return nil, RDPConstants::RDP_NEG_FAILURE[failure_code]
396
else
397
return nil, "Unknown Negotiation Response code: #{response_code}"
398
end
399
end
400
401
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/927de44c-7fe8-4206-a14f-e5517dc24b1c
402
# Parse Server MCS Connect Response PUD - 2.2.1.4
403
def rdp_parse_connect_response(pkt)
404
ptr = 0
405
rdp_pkt = pkt[0x49..pkt.length]
406
407
while ptr < rdp_pkt.length
408
header_type = rdp_pkt[ptr..ptr + 1]
409
header_length = rdp_pkt[ptr + 2..ptr + 3].unpack("S<")[0]
410
411
if header_type == "\x02\x0c"
412
413
server_random = rdp_pkt[ptr + 20..ptr + 51]
414
public_exponent = rdp_pkt[ptr + 84..ptr + 87]
415
416
rsa_magic = rdp_pkt[ptr + 68..ptr + 71]
417
if rsa_magic != "RSA1"
418
print_error("Server cert isn't RSA, this scenario isn't supported (yet).")
419
raise RdpCommunicationError
420
end
421
422
bitlen = rdp_pkt[ptr + 72..ptr + 75].unpack("L<")[0] - 8
423
modulus = rdp_pkt[ptr + 88..ptr + 87 + bitlen]
424
end
425
426
ptr += header_length
427
end
428
429
# vprint_status("SERVER_MODULUS: #{bin_to_hex(modulus)}")
430
# vprint_status("SERVER_EXPONENT: #{bin_to_hex(public_exponent)}")
431
# vprint_status("SERVER_RANDOM: #{bin_to_hex(server_random)}")
432
433
rsmod = bytes_to_bignum(modulus)
434
rsexp = bytes_to_bignum(public_exponent)
435
rsran = bytes_to_bignum(server_random)
436
437
# vprint_status("MODULUS = #{bin_to_hex(modulus)} - #{rsmod.to_s}")
438
# vprint_status("EXPONENT = #{bin_to_hex(public_exponent)} - #{rsexp.to_s}")
439
# vprint_status("SVRANDOM = #{bin_to_hex(server_random)} - #{rsran.to_s}")
440
441
return rsmod, rsexp, rsran, server_random, bitlen
442
end
443
444
#
445
# Encryption: Standard RDP Security
446
#
447
448
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7c61b54e-f6cd-4819-a59a-daf200f6bf94
449
# mac_salt_key = "W\x13\xc58\x7f\xeb\xa9\x10*\x1e\xddV\x96\x8b[d"
450
# data_content = "\x12\x00\x17\x00\xef\x03\xea\x03\x02\x00\x00\x01\x04\x00$\x00\x00\x00"
451
# hmac = rdp_hmac(mac_salt_key, data_content) # == hexlified: "22d5aeb486994a0c785dc929a2855923"
452
def rdp_hmac(mac_salt_key, data_content)
453
sha1 = Digest::SHA1.new
454
md5 = Digest::MD5.new
455
456
pad1 = "\x36" * 40
457
pad2 = "\x5c" * 48
458
459
sha1 << mac_salt_key
460
sha1 << pad1
461
sha1 << [data_content.length].pack('<L')
462
sha1 << data_content
463
464
md5 << mac_salt_key
465
md5 << pad2
466
md5 << [sha1.hexdigest].pack("H*")
467
468
[md5.hexdigest].pack("H*")
469
end
470
471
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/705f9542-b0e3-48be-b9a5-cf2ee582607f
472
# SaltedHash(S, I) = MD5(S + SHA(I + S + ClientRandom + ServerRandom))
473
def rdp_salted_hash(s_bytes, i_bytes, client_random_bytes, server_random_bytes)
474
sha1 = Digest::SHA1.new
475
md5 = Digest::MD5.new
476
477
sha1 << i_bytes
478
sha1 << s_bytes
479
sha1 << client_random_bytes
480
sha1 << server_random_bytes
481
482
md5 << s_bytes
483
md5 << [sha1.hexdigest].pack("H*")
484
485
[md5.hexdigest].pack("H*")
486
end
487
488
# FinalHash(K) = MD5(K + ClientRandom + ServerRandom)
489
def rdp_final_hash(k, client_random_bytes, server_random_bytes)
490
md5 = Digest::MD5.new
491
492
md5 << k
493
md5 << client_random_bytes
494
md5 << server_random_bytes
495
496
[md5.hexdigest].pack("H*")
497
end
498
499
def rdp_calculate_rc4_keys(client_random, server_random)
500
# preMasterSecret = First192Bits(ClientRandom) + First192Bits(ServerRandom)
501
preMasterSecret = client_random[0..23] + server_random[0..23]
502
503
# PreMasterHash(I) = SaltedHash(preMasterSecret, I)
504
# MasterSecret = PreMasterHash(0x41) + PreMasterHash(0x4242) + PreMasterHash(0x434343)
505
masterSecret = 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)
506
507
# MasterHash(I) = SaltedHash(MasterSecret, I)
508
# SessionKeyBlob = MasterHash(0x58) + MasterHash(0x5959) + MasterHash(0x5A5A5A)
509
sessionKeyBlob = 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)
510
511
# InitialClientDecryptKey128 = FinalHash(Second128Bits(SessionKeyBlob))
512
initialClientDecryptKey128 = rdp_final_hash(sessionKeyBlob[16..31], client_random, server_random)
513
514
# InitialClientEncryptKey128 = FinalHash(Third128Bits(SessionKeyBlob))
515
initialClientEncryptKey128 = rdp_final_hash(sessionKeyBlob[32..47], client_random, server_random)
516
517
mac_key = sessionKeyBlob[0..15]
518
519
return initialClientEncryptKey128, initialClientDecryptKey128, mac_key, sessionKeyBlob
520
end
521
522
def rsa_encrypt(bignum, rsexp, rsmod)
523
(bignum ** rsexp) % rsmod
524
end
525
526
def rdp_rc4_crypt(rc4obj, data)
527
rc4obj.encrypt(data)
528
end
529
530
def bytes_to_bignum(bytes_val, order = "little")
531
bytes = bin_to_hex(bytes_val)
532
if order == "little"
533
bytes = bytes.scan(/../).reverse.join('')
534
end
535
s = "0x" + bytes
536
s.to_i(16)
537
end
538
539
# https://www.ruby-forum.com/t/integer-to-byte-string-speed-improvements/67110
540
def int_to_bytestring( int_val, num_chars = nil )
541
unless num_chars
542
bits_needed = Math.log(int_val) / Math.log(2)
543
num_chars = ( bits_needed / 8.0 ).ceil
544
end
545
if pack_code = { 1 => 'C', 2 => 'S', 4 => 'L' }[num_chars]
546
[int_val].pack(pack_code)
547
else
548
a = (0..(num_chars)).map{ |i|
549
(( int_val >> i*8 ) & 0xFF ).chr
550
}.join
551
a[0..-2] # seems legit lol
552
end
553
end
554
555
def bin_to_hex(str_val)
556
str_val.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join
557
end
558
559
560
#
561
# Protocol Data Unit definitions and helpers
562
#
563
564
# Build the X.224 packet, encrypt with Standard RDP Security as needed
565
# default channel_id = 0x03eb = 1003
566
def rdp_build_pkt(data, channel_id = "\x03\xeb", client_info = false)
567
flags = 0
568
flags |= 0b1000 if @rdp_sec # Set SEC_ENCRYPT
569
flags |= 0b1000000 if client_info # Set SEC_INFO_PKT
570
571
pdu = ""
572
573
# TS_SECURITY_HEADER - 2.2.8.1.1.2.1
574
# Send when the packet is encrypted w/ Standard RDP Security and in all Client Info PDUs
575
if client_info || @rdp_sec
576
pdu << [flags].pack("S<") # flags "\x48\x00" = SEC_INFO_PKT | SEC_ENCRYPT
577
pdu << "\x00\x00" # flagsHi
578
end
579
580
if @rdp_sec
581
# Encrypt the payload with RDP Standard Encryption
582
pdu << rdp_hmac(@hmackey, data)[0..7]
583
pdu << rdp_rc4_crypt(@rc4enckey, data)
584
else
585
pdu << data
586
end
587
588
user_data_len = pdu.length
589
udl_with_flag = 0x8000 | user_data_len
590
591
pkt = "\x64" # sendDataRequest
592
pkt << "\x00\x08" # intiator userId .. TODO: for a functional client this isn't static
593
pkt << channel_id # channelId
594
pkt << "\x70" # dataPriority
595
pkt << [udl_with_flag].pack("S>")
596
pkt << pdu
597
598
build_data_tpdu(pkt)
599
end
600
601
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/6c074267-1b32-4ceb-9496-2eb941a23e6b
602
# Virtual Channel PDU 2.2.6.1
603
def build_virtual_channel_pdu(flags, data)
604
data_len = data.length
605
606
[data_len].pack("L<") + # length
607
[flags].pack("L<") + # flags
608
data
609
end
610
611
# Builds x.224 Data (DT) TPDU - Section 13.7
612
def build_data_tpdu(data)
613
tpkt_length = data.length + 7
614
615
"\x03\x00" + # TPKT Header version 03, reserved 0
616
[tpkt_length].pack("S>") + # TPKT length
617
"\x02\xf0\x80" + # X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)
618
data
619
end
620
621
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/18a27ef9-6f9a-4501-b000-94b1fe3c2c10
622
# Client X.224 Connect Request PDU - 2.2.1.1
623
def pdu_negotiation_request(user_name = "", requested_protocols = 0)
624
# Blank username is ok, nil = random
625
user_name = Rex::Text.rand_text_alpha(12) if user_name.nil?
626
tpkt_len = user_name.length + 38
627
x224_len = user_name.length + 33
628
629
"\x03\x00" + # TPKT Header version 03, reserved 0
630
[tpkt_len].pack("S>") + # TPKT length: 43
631
[x224_len].pack("C") + # X.224 LengthIndicator
632
"\xe0" + # X.224 Type: Connect Request
633
"\x00\x00" + # dst reference
634
"\x00\x00" + # src reference
635
"\x00" + # class and options
636
# cookie - literal 'Cookie: mstshash='
637
"\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d\x73\x74\x73\x68\x61\x73\x68\x3d" +
638
user_name + # Identifier "username"
639
"\x0d\x0a" + # cookie terminator
640
"\x01\x00" + # Type: RDP Negotiation Request ( 0x01 )
641
"\x08\x00" + # Length
642
[requested_protocols].pack('L<') # requestedProtocols
643
end
644
645
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/db6713ee-1c0e-4064-a3b3-0fac30b4037b
646
def pdu_connect_initial(channels, selected_proto = 0, host_name = "rdesktop")
647
# After negotiating TLS or NLA the connectInitial packet needs to include the
648
# protocol selection that the server indicated in its Negotiation Response
649
650
pdu = [
651
"\x7f\x65", # T.125 Connect-Initial (BER: Application 101)
652
ber_data(
653
"\x04\x01\x01", # CallingDomainSelector: 1 (BER: OctetString)
654
"\x04\x01\x01", # CalledDomainSelector: 1 (BER: OctetString)
655
"\x01\x01\xff", # UpwaredFlag: True (BER: boolean)
656
657
# TargetParamenters
658
encode_domain_selector(
659
max_chan_ids: 0x22,
660
max_user_ids: 0x2
661
),
662
# MinimumParameters
663
encode_domain_selector(
664
max_chan_ids: 0x1,
665
max_user_ids: 0x1,
666
max_token_ids: 0x1,
667
max_mcspdu_size: 0x0420
668
),
669
# MaximumParameters
670
encode_domain_selector(
671
max_chan_ids: 0xffff,
672
max_user_ids: 0xfc17,
673
max_token_ids: 0xffff
674
),
675
# UserData
676
ber_octet_string(
677
# T.124 GCC Connection Data (ConnectData)- PER Encoding used
678
per_object(oid(0, 0, 20, 124, 0, 1)),
679
per_data(
680
conf_create_req(),
681
per_data(
682
cs_core_data(client_name: host_name, selected_proto: selected_proto),
683
cs_cluster_data(),
684
cs_security_data(),
685
cs_network_data(channels)
686
)
687
)
688
)
689
)
690
].join('')
691
692
build_data_tpdu(pdu)
693
end
694
695
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/04c60697-0d9a-4afd-a0cd-2cc133151a9c
696
# Client MCS Erect Domain Request PDU - 2.2.1.5
697
def pdu_erect_domain_request
698
pdu =
699
"\x04" + # T.125 ErectDomainRequest
700
"\x01\x00" + # subHeight - length 1, value 0
701
"\x01\x00" # subInterval - length 1, value 0
702
703
build_data_tpdu(pdu)
704
end
705
706
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/f5d6a541-9b36-4100-b78f-18710f39f247\
707
# Client MCS Attach User Request PDU - 2.2.1.6
708
def pdu_attach_user_request
709
pdu = "\x28" # T.125 AttachUserRequest
710
711
build_data_tpdu(pdu)
712
end
713
714
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/64564639-3b2d-4d2c-ae77-1105b4cc011b
715
# Client MCS Channel Join Request PDU -2.2.1.8
716
def pdu_channel_join_request(user1, channel_id)
717
pdu =
718
"\x38" + # T.125 ChannelJoinRequest
719
[user1, channel_id].pack("nn")
720
721
build_data_tpdu(pdu)
722
end
723
724
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9cde84cd-5055-475a-ac8b-704db419b66f
725
# Client Security Exchange PDU - 2.2.1.10
726
def pdu_security_exchange(rcran, rsexp, rsmod, bitlen)
727
encrypted_rcran_bignum = rsa_encrypt(rcran, rsexp, rsmod)
728
encrypted_rcran = int_to_bytestring(encrypted_rcran_bignum)
729
730
bitlen += 8 # Pad with size of TS_SECURITY_PACKET header
731
732
userdata_length = 8 + bitlen
733
userdata_length_low = userdata_length & 0xFF
734
userdata_length_high = userdata_length / 256
735
flags = 0x80 | userdata_length_high
736
737
pdu =
738
"\x64" + # T.125 sendDataRequest
739
"\x00\x08" + # intiator userId
740
"\x03\xeb" + # channelId = 1003
741
"\x70" + # dataPriority = high, segmentation = begin | end
742
[flags].pack("C") +
743
[userdata_length_low].pack("C") + # UserData length
744
# TS_SECURITY_PACKET - 2.2.1.10.1
745
"\x01\x00" + # securityHeader flags
746
"\x00\x00" + # securityHeader flagsHi
747
[bitlen].pack("L<") + # TS_ length
748
encrypted_rcran + # encryptedClientRandom - 64 bytes
749
"\x00\x00\x00\x00\x00\x00\x00\x00" # 8 bytes rear padding (always present)
750
751
build_data_tpdu(pdu)
752
end
753
754
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/772d618e-b7d6-4cd0-b735-fa08af558f9d
755
# TS_INFO_PACKET - 2.2.1.11.1.1
756
def pdu_client_info(user_name, domain_name = "", ip_address = "")
757
# Max len for 4.0/6.0 servers is 44 bytes including terminator
758
# Max len for all other versions is 512 including terminator
759
# We're going to limit to 44 (21 chars + null -> unicode) here.
760
# Blank username is ok, nil = random
761
user_name = Rex::Text.rand_text_alpha(10) if user_name.nil?
762
user_unicode = Rex::Text.to_unicode(user_name[0..20], 'utf-16le')
763
uname_len = user_unicode.length
764
765
# Domain can can be, and for rdesktop typically is, empty.
766
# Max len for 4.0/5.0 servers is 52 including terminator
767
# Max len for all other versions is 512 including terminator
768
# We're going to limit to 52 (25 chars + null -> unicode) here.
769
domain_unicode = Rex::Text.to_unicode(domain_name[0..24], 'utf-16le')
770
domain_len = domain_unicode.length
771
772
# This address value is primarily used to reduce the fields by which this
773
# module can be fingerprinted. It doesn't show up in Windows logs.
774
# clientAddress + null terminator
775
ip_unicode = Rex::Text.to_unicode(ip_address, 'utf-16le') + "\x00\x00"
776
ip_len = ip_unicode.length
777
778
"\x00\x00\x00\x00" + # CodePage
779
"\x33\x01\x00\x00" + # flags - INFO_MOUSE, INFO_DISABLECTRLALTDEL, INFO_UNICODE, INFO_MAXIMIZESHELL, INFO_ENABLEWINDOWSKEY
780
[domain_len].pack("S<") + # cbDomain (length value) - EXCLUDES null terminator
781
[uname_len].pack("S<") + # cbUserName (length value) - EXCLUDES null terminator
782
"\x00\x00" + # cbPassword (length value)
783
"\x00\x00" + # cbAlternateShell (length value)
784
"\x00\x00" + # cbWorkingDir (length value)
785
[domain_unicode].pack("a*") + # Domain
786
"\x00\x00" + # Domain null terminator, EXCLUDED from value of cbDomain
787
[user_unicode].pack("a*") + # UserName
788
"\x00\x00" + # UserName null terminator, EXCLUDED FROM value of cbUserName
789
"\x00\x00" + # Password - empty
790
"\x00\x00" + # AlternateShell - empty
791
"\x00\x00" + # WorkingDir - empty
792
# TS_EXTENDED_INFO_PACKET - 2.2.1.11.1.1.1
793
"\x02\x00" + # clientAddressFamily - AF_INET - FIXFIX - detect and set dynamically
794
[ip_len].pack("S<") + # cbClientAddress (length value) - INCLUDES terminator ... for reasons.
795
[ip_unicode].pack("a*") + # clientAddress (unicode + null terminator (unicode)
796
"\x3c\x00" + # cbClientDir (length value): 60
797
# clientDir - 'C:\WINNT\System32\mstscax.dll' + null terminator
798
"\x3c\x00\x43\x00\x3a\x00\x5c\x00\x57\x00\x49\x00\x4e\x00\x4e\x00" + #
799
"\x54\x00\x5c\x00\x53\x00\x79\x00\x73\x00\x74\x00\x65\x00\x6d\x00" + #
800
"\x33\x00\x32\x00\x5c\x00\x6d\x00\x73\x00\x74\x00\x73\x00\x63\x00" + #
801
"\x61\x00\x78\x00\x2e\x00\x64\x00\x6c\x00\x6c\x00\x00\x00" + #
802
# clientTimeZone - TS_TIME_ZONE struct - 172 bytes
803
# These are the default values for rdesktop
804
"\xa4\x01\x00\x00" + # Bias
805
# StandardName - 'GTB,normaltid'
806
"\x47\x00\x54\x00\x42\x00\x2c\x00\x20\x00\x6e\x00\x6f\x00\x72\x00" + #
807
"\x6d\x00\x61\x00\x6c\x00\x74\x00\x69\x00\x64\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #
810
"\x00\x00\x0a\x00\x00\x00\x05\x00\x03\x00\x00\x00\x00\x00\x00\x00" + # StandardDate - Oct 5
811
"\x00\x00\x00\x00" + # StandardBias
812
# DaylightName - 'GTB,sommartid'
813
"\x47\x00\x54\x00\x42\x00\x2c\x00\x20\x00\x73\x00\x6f\x00\x6d\x00" + #
814
"\x6d\x00\x61\x00\x72\x00\x74\x00\x69\x00\x64\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #
817
"\x00\x00\x03\x00\x00\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00" + # DaylightDate - Mar 3
818
"\xc4\xff\xff\xff" + # DaylightBias
819
"\x00\x00\x00\x00" + # clientSessionId
820
"\x27\x00\x00\x00" + # performanceFlags
821
"\x00\x00" # cbAutoReconnectCookie
822
end
823
824
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/73d01865-2eae-407f-9b2c-87e31daac471
825
# Share Control Header - TS_SHARECONTROLHEADER - 2.2.8.1.1.1.1
826
def build_share_control_header(type, data)
827
total_len = data.length + 6
828
829
[total_len].pack("S<") + # totalLength - includes all headers
830
[type].pack("S<") + # pduType - flags 16 bit, unsigned
831
"\xf1\x03" + # PDUSource: 0x03f1 = 1009
832
data
833
end
834
835
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4b5d4c0d-a657-41e9-9c69-d58632f46d31
836
# Share Data Header - TS_SHAREDATAHEADER - 2.2.8.1.1.1.2
837
def build_share_data_header(type, data)
838
uncompressed_len = data.length + 4
839
840
"\xea\x03\x01\x00" + # shareId: 66538
841
"\x00" + # pad1
842
"\x01" + # streamID: 1
843
[uncompressed_len].pack("S<") + # uncompressedLength - 16 bit, unsigned int
844
[type].pack("C") + # pduType2 - 8 bit, unsigned int - 2.2.8.1.1.2
845
"\x00" + # compressedType: 0
846
"\x00\x00" + # compressedLength: 0
847
data
848
end
849
850
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9d1e1e21-d8b4-4bfd-9caf-4b72ee91a7135
851
# Control Cooperate - TC_CONTROL_PDU 2.2.1.15
852
def pdu_client_control_cooperate
853
pdu =
854
"\x04\x00" + # action: 4 - CTRLACTION_COOPERATE
855
"\x00\x00" + # grantId: 0
856
"\x00\x00\x00\x00" # controlId: 0
857
858
# pduType2 = 0x14 = 20 - PDUTYPE2_CONTROL
859
data_header = build_share_data_header(0x14, pdu)
860
861
# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
862
build_share_control_header(0x17, data_header)
863
end
864
865
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4f94e123-970b-4242-8cf6-39820d8e3d35
866
# Control Request - TC_CONTROL_PDU 2.2.1.16
867
def pdu_client_control_request
868
pdu =
869
"\x01\x00" + # action: 1 - CTRLACTION_REQUEST_CONTROL
870
"\x00\x00" + # grantId: 0
871
"\x00\x00\x00\x00" # controlId: 0
872
873
# pduType2 = 0x14 = 20 - PDUTYPE2_CONTROL
874
data_header = build_share_data_header(0x14, pdu)
875
876
# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
877
build_share_control_header(0x17, data_header)
878
end
879
880
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7067da0d-e318-4464-88e8-b11509cf0bd9
881
# Client Font List - TS_FONT_LIST_PDU - 2.2.1.18
882
def pdu_client_font_list
883
pdu =
884
"\x00\x00" + # numberFonts: 0
885
"\x00\x00" + # totalNumberFonts: 0
886
"\x03\x00" + # listFlags: 3 (FONTLIST_FIRST | FONTLIST_LAST)
887
"\x32\x00" # entrySize: 50
888
889
# pduType2 = 0x27 = 29 - PDUTYPE2_FONTLIST
890
data_header = build_share_data_header(0x27, pdu)
891
892
# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
893
build_share_control_header(0x17, data_header)
894
end
895
896
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/5186005a-36f5-4f5d-8c06-968f28e2d992
897
# Client Synchronize - TS_SYNCHRONIZE_PDU - 2.2.1.19 / 2.2.14.1
898
def pdu_client_synchronize(target_user = 0)
899
pdu =
900
"\x01\x00" + # messageType: 1 SYNCMSGTYPE_SYNC
901
[target_user].pack("S<") # targetUser, 16 bit, unsigned.
902
903
# pduType2 = 0x1f = 31 - PDUTYPE2_SCYNCHRONIZE
904
data_header = build_share_data_header(0x1f, pdu)
905
906
# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
907
build_share_control_header(0x17, data_header)
908
end
909
910
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4e9722c3-ad83-43f5-af5a-529f73d88b48
911
# Confirm Active PDU Data - TS_CONFIRM_ACTIVE_PDU - 2.2.1.13.2.1
912
def pdu_client_confirm_active
913
pdu =
914
"\xea\x03\x01\x00" + # shareId: 66538
915
"\xea\x03" + # originatorId
916
"\x06\x00" + # lengthSourceDescriptor: 6
917
"\x8e\x01" + # lengthCombinedCapabilities: 398
918
"\x4d\x53\x54\x53\x43\x00" + # SourceDescriptor: 'MSTSC'
919
"\x0e\x00" + # numberCapabilities: 14
920
"\x00\x00" + # pad2Octets
921
"\x01\x00" + # capabilitySetType: 1 - TS_GENERAL_CAPABILITYSET
922
"\x18\x00" + # lengthCapability: 24
923
"\x01\x00\x03\x00\x00\x02\x00\x00\x00\x00\x0d\x04\x00\x00\x00\x00" + #
924
"\x00\x00\x00\x00" + #
925
"\x02\x00" + # capabilitySetType: 2 - TS_BITMAP_CAPABILITYSET
926
"\x1c\x00" + # lengthCapability: 28
927
"\x10\x00\x01\x00\x01\x00\x01\x00\x20\x03\x58\x02\x00\x00\x01\x00" + #
928
"\x01\x00\x00\x00\x01\x00\x00\x00" + #
929
"\x03\x00" + # capabilitySetType: 3 - TS_ORDER_CAPABILITYSET
930
"\x58\x00" + # lengthCapability: 88
931
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #
932
"\x00\x00\x00\x00\x01\x00\x14\x00\x00\x00\x01\x00\x47\x01\x2a\x00" + #
933
"\x01\x01\x01\x01\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x01\x00" + #
934
"\x00\x00\x00\x00\x01\x01\x01\x00\x00\x01\x01\x01\x00\x00\x00\x00" + #
935
"\xa1\x06\x00\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00\x00" + #
936
"\xe4\x04\x00\x00\x13\x00\x28\x00\x00\x00\x00\x03\x78\x00\x00\x00" + #
937
"\x78\x00\x00\x00\x50\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #
938
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #
939
"\x08\x00" + # capabilitySetType: 8 - TS_POINTER_CAPABILITYSET
940
"\x0a\x00" + # lengthCapability: 10
941
"\x01\x00\x14\x00\x14\x00" + #
942
"\x0a\x00" + # capabilitySetType: 10 - TS_COLORTABLE_CAPABILITYSET
943
"\x08\x00" + # lengthCapability: 8
944
"\x06\x00\x00\x00" + #
945
"\x07\x00" + # capabilitySetType: 7 - TSWINDOWACTIVATION_CAPABILITYSET
946
"\x0c\x00" + # lengthCapability: 12
947
"\x00\x00\x00\x00\x00\x00\x00\x00" + #
948
"\x05\x00" + # capabilitySetType: 5 - TS_CONTROL_CAPABILITYSET
949
"\x0c\x00" + # lengthCapability: 12
950
"\x00\x00\x00\x00\x02\x00\x02\x00" + #
951
"\x09\x00" + # capabilitySetType: 9 - TS_SHARE_CAPABILITYSET
952
"\x08\x00" + # lengthCapability: 8
953
"\x00\x00\x00\x00" + #
954
"\x0f\x00" + # capabilitySetType: 15 - TS_BRUSH_CAPABILITYSET
955
"\x08\x00" + # lengthCapability: 8
956
"\x01\x00\x00\x00" + #
957
"\x0d\x00" + # capabilitySetType: 13 - TS_INPUT_CAPABILITYSET
958
"\x58\x00" + # lengthCapability: 88
959
"\x01\x00\x00\x00\x09\x04\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" + #
960
"\x0c\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + #
964
"\x00\x00\x00\x00" + #
965
"\x0c\x00" + # capabilitySetType: 12 - TS_SOUND_CAPABILITYSET
966
"\x08\x00" + # lengthCapability: 8
967
"\x01\x00\x00\x00" + #
968
"\x0e\x00" + # capabilitySetType: 14 - TS_FONT_CAPABILITYSET
969
"\x08\x00" + # lengthCapability: 8
970
"\x01\x00\x00\x00" + #
971
"\x10\x00" + # capabilitySetType: 16 - TS_GLYPHCAChE_CAPABILITYSET
972
"\x34\x00" + # lengthCapability: 52
973
"\xfe\x00\x04\x00\xfe\x00\x04\x00\xfe\x00\x08\x00\xfe\x00\x08\x00" + #
974
"\xfe\x00\x10\x00\xfe\x00\x20\x00\xfe\x00\x40\x00\xfe\x00\x80\x00" + #
975
"\xfe\x00\x00\x01\x40\x00\x00\x08\x00\x01\x00\x01\x02\x00\x00\x00"
976
977
# type = 0x13 = TS_PROTOCOL_VERSION | PDUTYPE_CONFIRMACTIVEPDU
978
build_share_control_header(0x13, pdu)
979
end
980
981
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/ff7f06f8-0dcf-4c8d-be1f-596ae60c4396
982
# Client Input Event Data - TS_INPUT_PDU_DATA - 2.2.8.1.1.3.1
983
def pdu_client_input_event_sychronize
984
pdu =
985
"\x01\x00" + # numEvents: 1
986
"\x00\x00" + # pad2Octets
987
"\x00\x00\x00\x00" + # eventTime
988
"\x00\x00" + # messageType: 0 - INPUT_EVENT_SYNC
989
# TS_SYNC_EVENT 202.8.1.1.3.1.1.5
990
"\x00\x00" + # pad2Octets
991
"\x00\x00\x00\x00" # toggleFlags
992
993
# pduType2 = 0x1c = 28 - PDUTYPE2_INPUT
994
data_header = build_share_data_header(0x1c, pdu)
995
996
# type = 0x17 = TS_PROTOCOL_VERSION | PDUTYPE_DATAPDU
997
build_share_control_header(0x17, data_header)
998
end
999
1000
#
1001
# Non-RDP protocol helper methods
1002
#
1003
1004
# Create a new SSL session on the existing socket.
1005
# Stolen from exploit/smtp_deliver.rb
1006
def swap_sock_plain_to_ssl
1007
ctx = OpenSSL::SSL::SSLContext.new
1008
ctx.min_version = OpenSSL::SSL::TLS1_VERSION
1009
ssl = OpenSSL::SSL::SSLSocket.new(self.rdp_sock, ctx)
1010
1011
ssl.connect
1012
1013
self.rdp_sock.extend(Rex::Socket::SslTcp)
1014
self.rdp_sock.sslsock = ssl
1015
self.rdp_sock.sslctx = ctx
1016
end
1017
1018
protected
1019
1020
def encode_domain_selector(
1021
max_chan_ids: 0,
1022
max_user_ids: 0,
1023
max_token_ids: 0,
1024
num_priorities: 1,
1025
min_throughput: 0,
1026
max_height: 1,
1027
max_mcspdu_size: 65535,
1028
protocol_ver: 2
1029
)
1030
1031
body = [
1032
ber_int(max_chan_ids),
1033
ber_int(max_user_ids),
1034
ber_int(max_token_ids),
1035
ber_int(num_priorities),
1036
ber_int(min_throughput),
1037
ber_int(max_height),
1038
ber_int(max_mcspdu_size),
1039
ber_int(protocol_ver)
1040
].join('')
1041
1042
result = [
1043
"\x30",
1044
[body.length].pack('C'),
1045
body
1046
].join('')
1047
1048
result
1049
end
1050
1051
def per_object(*ds)
1052
body = ds.join('')
1053
1054
result = [
1055
"\x00",
1056
[body.length].pack('C'),
1057
body
1058
].join('')
1059
1060
result
1061
end
1062
1063
def per_data(*ds)
1064
data = ds.join('')
1065
result = ''
1066
if data.length < 0x4000
1067
result = [data.length | 0x8000].pack('S>') + data
1068
else
1069
result = "\xA2" + [data.length].pack('S>') + data
1070
end
1071
1072
result
1073
end
1074
1075
def cs_cluster_data(
1076
flags: RDPConstants::REDIRECTION_SUPPORTED | RDPConstants::REDIRECTION_VERSION3,
1077
session_id: 0
1078
)
1079
body = [flags, session_id].pack('<L<L')
1080
1081
result = [
1082
[0xc004, body.length + 4].pack('<S<S'),
1083
body
1084
].join('')
1085
1086
result
1087
end
1088
1089
def cs_security_data(
1090
encryption_methods: RDPConstants::ENCRYPTION_40BIT | RDPConstants::ENCRYPTION_128BIT,
1091
ext_encryption_methods: 0
1092
)
1093
body = [encryption_methods, ext_encryption_methods].pack('<L<L')
1094
1095
result = [
1096
[0xc002, body.length + 4].pack('<S<S'),
1097
body
1098
].join('')
1099
1100
result
1101
end
1102
1103
def cs_network_data(channels)
1104
chan_data = channels.map{ |c|
1105
[c[0].encode('ASCII')].pack('a8*') + [c[1]].pack('L')
1106
}.join('')
1107
1108
body = [
1109
[channels.length].pack('L'),
1110
chan_data
1111
].join('')
1112
1113
result = [
1114
[0xc003, body.length + 4].pack('<S<S'),
1115
body
1116
].join('')
1117
1118
result
1119
end
1120
1121
1122
def cs_core_data(
1123
version: 0x80004,
1124
width: 800,
1125
height: 600,
1126
keyboard: 1033, # English
1127
client_build: 2600,
1128
client_name: "rdesktop",
1129
keyboard_type: 4, # IBMEhanced 101/102
1130
keyboard_subtype: 0,
1131
keyboard_func_key: 12,
1132
serial_num: 0,
1133
client_product_id: 1,
1134
client_dig_product_id: "",
1135
selected_proto: 0
1136
)
1137
1138
client_name = Rex::Text.to_unicode(client_name[0..16], 'utf-16le')
1139
client_dig_product_id = Rex::Text.to_unicode(client_dig_product_id[0..32], 'utf-16le')
1140
1141
body = [
1142
[version, width, height].pack('<L<S<S'),
1143
"\x01\xca", # colour depth (8BPP)
1144
"\x03\xaa", # SASSequence
1145
[keyboard, client_build, client_name, keyboard_type].pack('<L<La32*'),
1146
[keyboard_type, keyboard_subtype, keyboard_func_key].pack('<L<L<L'),
1147
"\x00" * 64, # imeFileName
1148
"\x01\xca", # postBeta2ColorDepth (8BPP)
1149
[client_product_id, serial_num].pack('<S<L'),
1150
"\x18\x00", # highColorDepth: 24 bpp
1151
"\x07\x00", # supportedColorDepths: flag (24 bpp | 16 bpp | 15 bpp )
1152
"\x01\x00", # earlyCapabilityFlags: 1 (RNS_UD_CS_SUPPORT_ERRINFO_PDU)
1153
[client_dig_product_id].pack('a64*'),
1154
"\x00", # connectionType: 0
1155
"\x00", # pad1octet
1156
# serverSelectedProtocol - After negotiating TLS or CredSSP this value must
1157
# match the selectedProtocol value from the server's Negotiate Connection
1158
# confirm PDU that was sent before encryption was started.
1159
[selected_proto].pack('L<')
1160
].join('')
1161
1162
result = [
1163
[0xc001, body.length + 4].pack('<S<S'),
1164
body
1165
].join('')
1166
1167
result
1168
end
1169
1170
def conf_create_req(user_data_sets: 1, h221_key: "Duca")
1171
b2 = 0
1172
b2 |= 0x08 if user_data_sets > 0
1173
1174
b5 = 0x40
1175
b5 |= 0x80 if user_data_sets > 0
1176
1177
# TODO: add more flags here
1178
[
1179
"\x00",
1180
[b2].pack('C'),
1181
"\x00\x10\x00",
1182
[user_data_sets].pack('C'),
1183
[b5].pack('C'),
1184
"\x00",
1185
[h221_key.encode('ASCII')].pack('a*')
1186
].join('')
1187
end
1188
1189
def oid(itut, rec, t, t124, ver, desc)
1190
[(itut << 8) | rec, t, t124, ver, desc].pack('C*')
1191
end
1192
1193
def ber_octet_string(*ds)
1194
result = [
1195
"\x04",
1196
ber_data(ds)
1197
].join('')
1198
1199
result
1200
end
1201
1202
def ber_data(*ds)
1203
data = ds.join('')
1204
1205
result = [
1206
"\x82",
1207
[data.length].pack('S>'),
1208
data
1209
].join('')
1210
1211
result
1212
end
1213
1214
def ber_int(i)
1215
d = ''
1216
if i < (2 ** 8)
1217
d = [i].pack('C')
1218
elsif i < (2 ** 16)
1219
d = [i].pack('S>')
1220
else
1221
d = [i].pack('L>')
1222
end
1223
1224
"\x02" + [d.length].pack('C') + d
1225
end
1226
1227
def rdp_handle_packet(pkt)
1228
if pkt && pkt[0] == "\x03"
1229
if pkt[4..6] == "\x02\xf0\x80"
1230
if pkt[7] == "\x68"
1231
chan_user_id = pkt[8..9].unpack('S>')[0]
1232
chan_id = pkt[10..11].unpack('S>')[0]
1233
flags = pkt[18..21].unpack('<L')[0]
1234
data = pkt[22..pkt.length]
1235
rdp_on_channel_receive(pkt, chan_user_id, chan_id, flags, data)
1236
end
1237
end
1238
end
1239
end
1240
1241
def rdp_on_channel_receive(pkt, chan_user_id, chan_id, flags, data)
1242
ctype = data[0..1].unpack('S')[0]
1243
1244
if ctype == RDPConstants::RDPDR_CTYP_CORE
1245
opcode = data[2..3].unpack('S')[0]
1246
if opcode == RDPConstants::PAKID_CORE_SERVER_ANNOUNCE
1247
rdp_on_core_server_announce(pkt, chan_user_id, chan_id, flags, data)
1248
elsif opcode == RDPConstants::PAKID_CORE_SERVER_CAPABILITY
1249
rdp_on_core_server_capability(pkt, chan_user_id, chan_id, flags, data)
1250
elsif opcode == RDPConstants::PAKID_CORE_CLIENTID_CONFIRM
1251
rdp_on_core_client_id_confirm(pkt, chan_user_id, chan_id, flags, data)
1252
end
1253
end
1254
end
1255
1256
def rdp_on_core_server_announce(pkt, chan_user_id, chan_id, flags, data)
1257
vprint_status("Handling SERVER ANNOUNCE ...")
1258
rdpdr_client_announce_reply(pkt, chan_user_id, chan_id, flags, data)
1259
rdpdr_client_name_request(pkt, chan_user_id, chan_id, flags, data)
1260
end
1261
1262
def rdp_on_core_server_capability(pkt, chan_user_id, chan_id, flags, data)
1263
vprint_status("Handling SERVER CAPABILITY ...")
1264
# change opcode 1 byte to match server capabilities
1265
reply = [data[0..2], "\x43", data[4..data.length]].join('')
1266
1267
rdp_send_channel(chan_user_id, chan_id, reply)
1268
end
1269
1270
def rdp_on_core_client_id_confirm(pkt, chan_user_id, chan_id, flags, data)
1271
vprint_status("Handling CLIENT ID CONFIRM ...")
1272
rdpdr_client_device_list_announce_request(pkt, chan_user_id, chan_id, flags, data)
1273
end
1274
1275
def rdpdr_client_device_list_announce_request(pkt, chan_user_id, chan_id, flags, data)
1276
reply = [
1277
RDPConstants::RDPDR_CTYP_CORE,
1278
RDPConstants::PAKID_CORE_DEVICELIST_ANNOUNCE,
1279
0x0, # Device count
1280
].pack('SSL')
1281
1282
rdp_send_channel(chan_user_id, chan_id, reply)
1283
end
1284
1285
def rdpdr_client_announce_reply(pkt, chan_user_id, chan_id, flags, data)
1286
reply = [
1287
RDPConstants::RDPDR_CTYP_CORE,
1288
RDPConstants::PAKID_CORE_CLIENTID_CONFIRM,
1289
0x1, # Version Major
1290
0xc, # Version Minor
1291
0x2, # client ID (TODO: configure this? read it from the packet?
1292
].pack('SSSSL')
1293
1294
rdp_send_channel(chan_user_id, chan_id, reply)
1295
end
1296
1297
def rdpdr_client_name_request(pkt, chan_user_id, chan_id, flags, data)
1298
computer_name = Rex::Text.to_unicode("ethdev\x00", 'utf-16le')
1299
reply = [
1300
RDPConstants::RDPDR_CTYP_CORE,
1301
RDPConstants::PAKID_CORE_CLIENT_NAME,
1302
0x1, # Unicode flag
1303
0x0, # Code Page
1304
computer_name.length,
1305
computer_name,
1306
].pack('SSLLLa*')
1307
1308
rdp_send_channel(chan_user_id, chan_id, reply)
1309
end
1310
1311
attr_accessor :rdp_sock
1312
1313
attr_accessor :rdp_user_id
1314
1315
=begin
1316
# debug stuff
1317
def rdp_to_file(b, del = false)
1318
p = "/tmp/ruby-full.bin"
1319
::File.delete(p) if del && ::File.exist?(p)
1320
f = ::File.new(p, "ab")
1321
f.write(b)
1322
f.close
1323
end
1324
=end
1325
1326
end
1327
end
1328
1329
1330