AIGC标识 CVE-2019-0708(BlueKeep)Windows远程桌面RDP

import socket
import struct
import sys

def create_rdp_connection_request():
    """
    构建一个基础的 RDP Connection Request PDU。
    注意:这只是一个合法的、标准的 RDP 握手包,不包含任何漏洞利用载荷。
    """
    # RDP 协议版本 (5.0)
    rdp_version = 0x00080001
    
    # 构建 RDP Negotiation Request (可选,简化版直接发 Connection Request)
    # 这里我们构建一个最简化的 Initial Client Data PDU
    
    # TPKT (Transport Protocol Data Unit)
    # Length: 可变,稍后计算
    # Type: 0x0E (X.224 Connection Request)
    
    # X.224 Connection Request PDU
    # Length: 可变
    # Type: 0xE0
    # dst_ref, src_ref, class: 0x0000, 0x0000, 0x00
    
    # RDP Core Data
    # version: 0x00080001
    # clientName: 固定填充
    
    client_name = "TESTCLIENT\x00" * 2 # 填充至足够长度
    client_name_bytes = client_name.encode('ascii')[:32].ljust(32, b'\x00')
    
    # 构造一个简单的 RDP 初始包 (简化版,仅用于演示 TCP 连通性和端口识别)
    # 真实的 RDP 握手非常复杂,包含加密协商等步骤
    
    # 为了演示目的,我们发送一个符合 TPKT + X.224 CR 结构的包
    # 实际漏洞触发需要极其复杂的 Channel Definition 和 Memory Corruption 逻辑,此处省略
    
    # 这是一个标准的 X.224 Connection Request 头部
    x224_cr = bytearray()
    x224_cr.append(0x0e) # Length MSB (will be updated)
    x224_cr.append(0x00) # Length LSB
    x224_cr.append(0xe0) # Type: Connection Request
    x224_cr.append(0x00) # dst_ref
    x224_cr.append(0x00) # src_ref
    x224_cr.append(0x00) # class
    
    # RDP Negotiation Request (可选,通常用于协商 SSL/TLS)
    # 如果目标是旧系统且未强制 NLA,可能直接接受 RDP 加密
    
    # 组合包
    packet = bytes(x224_cr)
    
    # 更新长度 (TPKT length includes itself)
    # TPKT Header: 4 bytes (Length 2, Type 1, Reserved 1) - 但 X.224 CR 通常直接跟在 TCP 后或作为 TPKT 负载
    # 标准 TPKT:
    # Byte 0-1: Length (Big Endian)
    # Byte 2: Type (0x0E for CR if using X.224 directly over TCP without TPKT wrapper in some contexts, 
    # but usually TPKT wraps it. Let's use standard TPKT wrapping X.224 CR)
    
    # 重新构建:TPKT -> X.224 CR
    tpkt_length = len(x224_cr) + 4 # TPKT header is 4 bytes? No, TPKT is 4 bytes total including length field?
    # TPKT Format:
    # Version (1 byte): 0x03
    # Reserved (1 byte): 0x00
    # Length (2 bytes): Total length of TPKT including header
    
    # Let's stick to the most common raw RDP probe which is just checking if port is open and responds.
    # For a true protocol interaction, we need to send a valid RDP PDU.
    
    # Simplified valid RDP Initial PDU structure for detection
    # This is a non-exploit, standard handshake attempt.
    
    buf = bytearray()
    # TPKT Header
    buf.append(0x03) # Version
    buf.append(0x00) # Reserved
    # Length will be filled later
    
    # X.224 Connection Request
    buf.append(0x00) # Length MSB (placeholder)
    buf.append(0x00) # Length LSB (placeholder)
    buf.append(0xe0) # Type
    buf.append(0x00) # dst_ref
    buf.append(0x00) # src_ref
    buf.append(0x00) # class
    
    # RDP Negotiation Request (Optional, but good for modern servers)
    # Type: 0x01 (RDP_NEG_REQ)
    # Flags: 0x00
    # Length: 0x08
    # Protocols: 0x03 (SSL | HYBRID) or 0x00 (Standard RDP)
    
    neg_req = bytearray()
    neg_req.append(0x01) # Type
    neg_req.append(0x00) # Flags
    neg_req.append(0x08) # Length (LSB)
    neg_req.append(0x00) # Length (MSB)
    neg_req.append(0x03) # Protocols (SSL | HYBRID) - Try to negotiate secure
    neg_req.append(0x00)
    neg_req.append(0x00)
    neg_req.append(0x00)
    
    buf.extend(neg_req)
    
    # Update lengths
    total_len = len(buf)
    # TPKT Length (bytes 2-3 of TPKT, which are indices 2 and 3 in our buf if we started with version/reserved)
    # Wait, my buf structure:
    # 0: Ver
    # 1: Res
    # 2: Len MSB
    # 3: Len LSB
    # ...
    
    buf[2] = (total_len >> 8) & 0xFF
    buf[3] = total_len & 0xFF
    
    return bytes(buf)

def scan_rdp(target_ip, port=3389, timeout=5):
    """
    尝试连接目标 RDP 端口并发送基础握手包。
    """
    print(f"[*] 正在扫描 {target_ip}:{port} ...")
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        sock.connect((target_ip, port))
        print("[+] 端口开放,正在发送 RDP 握手请求...")
        
        request = create_rdp_connection_request()
        sock.send(request)
        
        # 接收响应
        response = sock.recv(1024)
        if response:
            print("[+] 收到响应:")
            print(f"    Hex: {response.hex()}")
            # 简单的响应解析
            if len(response) >= 4:
                if response[0] == 0x03 and response[2] == 0x00: # TPKT
                     if response[4] == 0x0e: # X.224 Connect Confirm
                         print("    类型: X.224 Connect Confirm (连接成功)")
                     elif response[4] == 0x02: # X.224 Disconnect
                         print("    类型: X.224 Disconnect (连接被拒绝/断开)")
        else:
            print("[-] 未收到响应")
            
        sock.close()
        return True
        
    except socket.timeout:
        print("[-] 连接超时")
        return False
    except ConnectionRefusedError:
        print("[-] 连接被拒绝")
        return False
    except Exception as e:
        print(f"[-] 发生错误: {e}")
        return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法: python rdp_scanner.py <target_ip>")
        sys.exit(1)
    
    target = sys.argv[1]
    scan_rdp(target)

CVE-2019-0708 的根本原因在于 Windows RDP 服务在处理特定虚拟通道(MS_T120)请求时存在“释放后重用”(Use-After-Free)漏洞。攻击者通过发送特制的 RDP 连接请求,在身份验证之前即可触发该漏洞。

nmap --script rdp-ntlm-info -p 3389 <目标IP>
  • 如果输出中包含 NTLM 相关信息且表明支持 CredSSP 或 NLA,则说明目标支持并可能启用了网络级身份验证。
  • 如果响应显示仅支持 Standard RDP Security 而不提及 NLA/CredSSP,则可能未启用。
posted @ 2026-09-22 17:04  LalaTina  阅读(15)  评论(0)    收藏  举报