4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / my_rdp.py PY
import socket
import ssl
import struct
import time
import sys
from importlib import util as importlib_util
from os import path

# ===================================================================================
# my_rdp.py - Pustaka Inti untuk RDP Fingerprinting & NTLM Info
# Versi 3.0 - Didesain ulang untuk keandalan maksimum
# ===================================================================================

# --- Konstanta Protokol ---
PROTOCOL_RDP = 0
PROTOCOL_SSL = 1
PROTOCOL_HYBRID = 2  # Target utama kita untuk CredSSP/NTLM
PROTOCOL_HYBRID_EX = 8

TYPE_RDP_NEG_REQ = 1
TYPE_RDP_NEG_RSP = 2
TYPE_RDP_NEG_FAILURE = 3

# --- Peta Versi OS dari NTLM ---
OsVersion = {
    "6.0.6002": "Windows Server 2008 SP2",
    "6.1.7601": "Windows 7 SP1 / Server 2008 R2 SP1",
    "6.2.9200": "Windows 8 / Server 2012",
    "6.3.9600": "Windows 8.1 / Server 2012 R2",
    "10.0.14393": "Windows 10, v1607 / Server 2016",
    "10.0.17763": "Windows 10, v1809 / Server 2019",
    "10.0.19041": "Windows 10, v2004", "10.0.19042": "Windows 10, v20H2",
    "10.0.19043": "Windows 10, v21H1", "10.0.19044": "Windows 10, v21H2",
    "10.0.19045": "Windows 10, v22H2", "10.0.20348": "Windows Server 2022",
    "10.0.22621": "Windows 11, v22H2", "10.0.22631": "Windows 11, v23H2",
    "10.0.25110": "Windows 10/11 Insider Preview" # Menambahkan versi dari contoh Anda
}

def NewReq(protocol, cookie="mhl-team"):
    """Membangun paket RDP Negotiation Request."""
    cookie_bytes = f"Cookie: mstshash={cookie}\r\n".encode()
    rdpNegReq = struct.pack('<BBHI', TYPE_RDP_NEG_REQ, 0, 8, protocol)
    data = cookie_bytes + rdpNegReq
    x224Crq = struct.pack('B', len(data) + 6) + b'\xe0\x00\x00\x00\x00\x00'
    tpktHeader = struct.pack('>BBH', 3, 0, len(x224Crq) + len(data))
    return tpktHeader + x224Crq + data

def ParseRdpResp(data):
    """Mem-parsing header respons RDP yang paling dasar."""
    if len(data) < 19:
        return None, None, None, "Response packet too short"
    rdp_neg_data = data[11:19]
    if len(rdp_neg_data) < 8:
        return None, None, None, "RDP Neg data too short"
    resp_type, _, _, result = struct.unpack('<BBHI', rdp_neg_data)
    return resp_type, None, result, None

def RdpWithNTLM(conn):
    """Mengekstrak informasi dari NTLM Challenge."""
    info = {}
    # Paket NTLM Negotiate Message
    NegotiatePacket = b'NTLMSSP\x00\x01\x00\x00\x00\xb7\x82\x08\xe2'
    
    # Bungkus paket NTLM di dalam TSRequest (CredSSP)
    ts_request_header = b'\x30\x81\x84\xa0\x03\x02\x01\x01\xa1\x81\x7c\x30\x81\x79\xa0\x81\x76\x04\x81\x73'
    full_packet = ts_request_header + NegotiatePacket
    
    try:
        conn.send(full_packet)
        response = conn.recv(4096)
    except Exception as e:
        return None, f"Send/Recv failed: {e}"

    challenge_offset = response.find(b'NTLMSSP\x00\x02')
    if challenge_offset == -1:
        return None, "NTLM Challenge not found in response"
    
    response = response[challenge_offset:]
    if len(response) < 56:
        return None, "NTLM Challenge packet too short"

    try:
        TargetNameLen, _, TargetNameBufferOffset = struct.unpack('<HH_I', response[12:20])
        Version = response[48:56]
        major, minor, build = struct.unpack('<BBH', Version[:4])
        
        ProductVersion = f"{major}.{minor}.{build}"
        info["Product_Version"] = ProductVersion
        info["OS"] = OsVersion.get(ProductVersion, "Unknown Windows Version")

        info["Target_Name"] = response[TargetNameBufferOffset:TargetNameBufferOffset + TargetNameLen].decode('utf-16le', 'ignore')

        TargetInfoLen, _, TargetInfoBufferOffset = struct.unpack('<HHI', response[40:48])
        av_pairs_bytes = response[TargetInfoBufferOffset : TargetInfoBufferOffset + TargetInfoLen]
        
        AvIDMap = {1: "NetBIOS_Computer_Name", 2: "NetBIOS_Domain_Name", 3: "DNS_Computer_Name", 4: "DNS_Domain_Name", 7: "System_Time"}
        curr_idx = 0
        while curr_idx + 4 <= len(av_pairs_bytes):
            av_id, av_len = struct.unpack('<HH', av_pairs_bytes[curr_idx : curr_idx + 4])
            if av_id == 0: break
            
            value_bytes = av_pairs_bytes[curr_idx + 4 : curr_idx + 4 + av_len]
            curr_idx += 4 + av_len
            
            if av_id in AvIDMap:
                field = AvIDMap[av_id]
                if av_id == 7:
                    unix_stamp = (struct.unpack('<Q', value_bytes)[0] / 10000000) - 11644473600
                    info[field] = time.strftime("%Y-%m-%d %H:%M:%S +0000 UTC", time.gmtime(unix_stamp))
                else:
                    info[field] = value_bytes.decode('utf-16le', 'ignore')
        return info, None
    except Exception as e:
        return None, f"NTLM parsing failed: {e}"

def rdp_check(ip, port, timeout=5, isscreen=False):
    """Fungsi utama yang didesain ulang untuk keandalan."""
    ret = {"target": f"{ip}:{port}"}
    conn = None
    try:
        # Tahap 1: Buka SATU koneksi dan langsung coba negosiasi HYBRID (CredSSP)
        conn = socket.create_connection((ip, port), timeout=timeout)
        req = NewReq(PROTOCOL_HYBRID)
        conn.sendall(req)
        response = conn.recv(1024)

        # Tahap 2: Periksa apakah server setuju menggunakan HYBRID
        resp_type, _, result, err = ParseRdpResp(response)
        
        if err is None and resp_type == TYPE_RDP_NEG_RSP and (result == PROTOCOL_HYBRID or result == PROTOCOL_HYBRID_EX):
            # BERHASIL! Server setuju. Jangan tutup koneksi.
            ret["flag"] = "PROTOCOL_HYBRID_EX" if result == PROTOCOL_HYBRID_EX else "PROTOCOL_HYBRID"
            
            # Tahap 3: Upgrade koneksi yang SAMA ke TLS
            ssl_conn = ssl.wrap_socket(conn, ssl_version=ssl.PROTOCOL_TLS)
            conn = None  # Koneksi asli sekarang dikelola oleh ssl_conn

            # Tahap 4: Ekstrak informasi NTLM melalui terowongan TLS
            ntlm_info, ntlm_err = RdpWithNTLM(ssl_conn)
            
            if ntlm_err: ret['error'] = ntlm_err
            if ntlm_info: ret.update(ntlm_info) # Gabungkan info NTLM ke hasil utama
        
        else:
            # Gagal negosiasi HYBRID. Target mungkin hanya RDP standar atau SSL.
            return {"target": f"{ip}:{port}", "error": "Target tidak mendukung CredSSP (HYBRID/NTLM)."}

    except socket.timeout:
        return None # Target tidak merespons dalam waktu yang ditentukan
    except Exception as e:
        return {"target": f"{ip}:{port}", "error": f"Terjadi kesalahan koneksi: {e}"}
    finally:
        # Selalu pastikan koneksi ditutup
        if conn:
            conn.close()

    # Hanya kembalikan hasil jika ada informasi yang berguna
    return ret if len(ret) > 1 else None