README.md
Rendering markdown...
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