编写TLS协议的suricata规则-测试(9001127-9001153)

1.主机B开启弱TLS服务

from __future__ import annotations
import argparse
import socket
import threading
def check_tcp_port_available(listen_ip: str, port: int) -> None:
    probe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        probe.bind((listen_ip, port))
    except OSError as exc:
        raise SystemExit(
            f"[server] TCP {listen_ip}:{port} is already in use. "
            f"Run `ss -ltnp | grep :{port}` on 10.10.10.2."
        ) from exc
    finally:
        probe.close()
def check_udp_port_available(listen_ip: str, port: int) -> None:
    probe = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        probe.bind((listen_ip, port))
    except OSError as exc:
        raise SystemExit(
            f"[server] UDP {listen_ip}:{port} is already in use. "
            f"Run `ss -lunp | grep :{port}` on 10.10.10.2."
        ) from exc
    finally:
        probe.close()
def recv_with_timeout(conn: socket.socket, timeout: float = 1.0) -> bytes:
    conn.settimeout(timeout)
    chunks: list[bytes] = []
    while True:
        try:
            data = conn.recv(4096)
        except socket.timeout:
            break
        if not data:
            break
        chunks.append(data)
        if len(data) < 4096:
            break
    return b"".join(chunks)
def tls_record(version: bytes, handshake_type: int, body: bytes) -> bytes:
    handshake = bytes([handshake_type]) + len(body).to_bytes(3, "big") + body
    return b"\x16" + version + len(handshake).to_bytes(2, "big") + handshake
def build_server_hello(version: bytes, cipher_suite: bytes) -> bytes:
    body = version + (b"\x11" * 32) + b"\x00" + cipher_suite + b"\x00"
    return tls_record(version, 0x02, body)
def build_certificate_record(version: bytes, oid_bytes: bytes) -> bytes:
    body = b"\x00\x00\x10" + b"\x30\x82" + b"\x01\x22" + b"\x30\x81" + oid_bytes + b"\x05\x00" + (b"\x41" * 24)
    return tls_record(version, 0x0B, body)
def build_server_key_exchange(version: bytes, curve_bytes: bytes) -> bytes:
    body = b"\x03" + curve_bytes + (b"\x22" * 24)
    return tls_record(version, 0x0C, body)
def build_http_response(body: bytes, content_type: str = "text/plain") -> bytes:
    headers = [
        b"HTTP/1.1 200 OK",
        b"Connection: close",
        f"Content-Length: {len(body)}".encode("ascii"),
        f"Content-Type: {content_type}".encode("ascii"),
        b"",
        b"",
    ]
    return b"\r\n".join(headers) + body
TLS_PROFILES: dict[str, bytes] = {
    "RC4_MD5_TLS10": build_server_hello(b"\x03\x01", b"\x00\x04"),
    "RC4_SHA": build_server_hello(b"\x03\x03", b"\x00\x05"),
    "DH_ANON_RC4": build_server_hello(b"\x03\x03", b"\x00\x18"),
    "ECDHE_ECDSA_RC4": build_server_hello(b"\x03\x03", b"\xC0\x07"),
    "ECDHE_RSA_RC4": build_server_hello(b"\x03\x03", b"\xC0\x11"),
    "DES3_RSA_TLS11": build_server_hello(b"\x03\x02", b"\x00\x0A"),
    "DES3_DHE_DSS": build_server_hello(b"\x03\x03", b"\x00\x13"),
    "DES3_DHE_RSA": build_server_hello(b"\x03\x03", b"\x00\x16"),
    "DES3_ECDHE_ECDSA": build_server_hello(b"\x03\x03", b"\xC0\x08"),
    "DES3_ECDHE_RSA": build_server_hello(b"\x03\x03", b"\xC0\x12"),
    "CERT_MD5": build_certificate_record(b"\x03\x03", b"\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x04"),
    "CERT_SHA1_RSA": build_certificate_record(b"\x03\x03", b"\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05"),
    "CERT_ECDSA_SHA1": build_certificate_record(b"\x03\x03", b"\x06\x07\x2A\x86\x48\xCE\x3D\x04\x01"),
    "WEAK_CURVE": build_server_key_exchange(b"\x03\x03", b"\x00\x10"),
    "RSA_AES128_SHA": build_server_hello(b"\x03\x03", b"\x00\x2F"),
    "RSA_AES256_SHA": build_server_hello(b"\x03\x03", b"\x00\x35"),
    "RSA_AES128_SHA256": build_server_hello(b"\x03\x03", b"\x00\x3C"),
    "RSA_AES256_SHA256": build_server_hello(b"\x03\x03", b"\x00\x3D"),
}
def parse_tls_profile(request: bytes) -> str:
    text = request.decode("latin-1", errors="ignore").strip()
    if text.startswith("PROFILE "):
        return text.split(" ", 1)[1].strip().upper()
    return text.upper()
def handle_tls_client(conn: socket.socket, addr: tuple[str, int], tls_port: int) -> None:
    try:
        request = recv_with_timeout(conn, 0.8)
        profile = parse_tls_profile(request) or "RC4_MD5_TLS10"
        payload = TLS_PROFILES.get(profile, TLS_PROFILES["RC4_MD5_TLS10"])
        conn.sendall(payload)
        print(f"[server:{tls_port}] tls profile={profile} from {addr[0]}:{addr[1]}")
    except Exception as exc:  # pragma: no cover - lab helper
        print(f"[server:{tls_port}] tls handler error from {addr}: {exc}")
    finally:
        try:
            conn.close()
        except OSError:
            pass
def parse_http_path(request: bytes) -> str:
    first_line = request.split(b"\r\n", 1)[0].decode("latin-1", errors="ignore")
    parts = first_line.split(" ")
    if len(parts) >= 2:
        return parts[1]
    return "/"
def handle_http_client(conn: socket.socket, addr: tuple[str, int], http_port: int) -> None:
    try:
        request = recv_with_timeout(conn, 1.0)
        path = parse_http_path(request)
        if path.startswith("/health"):
            body = b"ok\n"
        else:
            body = b"ok\n"
        conn.sendall(build_http_response(body))
        first_line = request.split(b"\r\n", 1)[0].decode("latin-1", errors="ignore")
        print(f"[server:{http_port}] {addr[0]}:{addr[1]} {first_line}")
    except Exception as exc:  # pragma: no cover - lab helper
        print(f"[server:{http_port}] http handler error from {addr}: {exc}")
    finally:
        try:
            conn.close()
        except OSError:
            pass
def recv_line(conn: socket.socket) -> bytes:
    data = bytearray()
    while not data.endswith(b"\n"):
        chunk = conn.recv(1)
        if not chunk:
            break
        data.extend(chunk)
    return bytes(data)
def handle_starttls_client(conn: socket.socket, addr: tuple[str, int], port: int) -> None:
    try:
        conn.settimeout(2.0)
        conn.sendall(b"220 weaktls-lab.example ESMTP ready\r\n")
        ehlo = recv_line(conn)
        if ehlo:
            conn.sendall(b"250-STARTTLS\r\n250 AUTH PLAIN LOGIN\r\n")
        auth_line = recv_line(conn)
        if auth_line:
            conn.sendall(b"235 2.7.0 accepted for test\r\n")
        print(
            f"[server:{port}] {addr[0]}:{addr[1]} "
            f"ehlo={ehlo.decode('latin-1', errors='ignore').strip()} "
            f"auth={auth_line.decode('latin-1', errors='ignore').strip()}"
        )
    except Exception as exc:  # pragma: no cover - lab helper
        print(f"[server:{port}] starttls handler error from {addr}: {exc}")
    finally:
        try:
            conn.close()
        except OSError:
            pass
def serve_tcp(listen_ip: str, port: int, stop_event: threading.Event, handler) -> None:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((listen_ip, port))
    sock.listen(128)
    sock.settimeout(1.0)
    print(f"[server:{port}] listening on {listen_ip}:{port}")
    try:
        while not stop_event.is_set():
            try:
                conn, addr = sock.accept()
            except socket.timeout:
                continue
            thread = threading.Thread(target=handler, args=(conn, addr, port), daemon=True)
            thread.start()
    finally:
        sock.close()
def serve_quic(listen_ip: str, port: int, stop_event: threading.Event) -> None:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((listen_ip, port))
    sock.settimeout(1.0)
    version_negotiation = b"\xC0\x00\x00\x00\x00\x01\x08testquic"
    print(f"[server:{port}/udp] listening on {listen_ip}:{port}")
    try:
        while not stop_event.is_set():
            try:
                data, addr = sock.recvfrom(4096)
            except socket.timeout:
                continue
            sock.sendto(version_negotiation, addr)
            print(f"[server:{port}/udp] {addr[0]}:{addr[1]} {len(data)} bytes")
    finally:
        sock.close()
def main() -> None:
    parser = argparse.ArgumentParser(description="Minimal live responder for weak_tls_followon_supplement.rules")
    parser.add_argument("--listen-ip", default="10.10.10.2")
    parser.add_argument("--tls-port", type=int, default=18443)
    parser.add_argument("--http-port", type=int, default=18080)
    parser.add_argument("--starttls-port", type=int, default=18125)
    parser.add_argument("--quic-port", type=int, default=18480)
    args = parser.parse_args()
    check_tcp_port_available(args.listen_ip, args.tls_port)
    check_tcp_port_available(args.listen_ip, args.http_port)
    check_tcp_port_available(args.listen_ip, args.starttls_port)
    check_udp_port_available(args.listen_ip, args.quic_port)
    stop_event = threading.Event()
    threads = [
        threading.Thread(target=serve_tcp, args=(args.listen_ip, args.tls_port, stop_event, handle_tls_client), daemon=True),
        threading.Thread(target=serve_tcp, args=(args.listen_ip, args.http_port, stop_event, handle_http_client), daemon=True),
        threading.Thread(target=serve_tcp, args=(args.listen_ip, args.starttls_port, stop_event, handle_starttls_client), daemon=True),
        threading.Thread(target=serve_quic, args=(args.listen_ip, args.quic_port, stop_event), daemon=True),
    ]
    for thread in threads:
        thread.start()
    try:
        for thread in threads:
            thread.join()
    except KeyboardInterrupt:
        print("\n[server] stopping")
        stop_event.set()
if __name__ == "__main__":
    main()

2.主机A发送请求

from __future__ import annotations
import argparse
import socket
import time
TLS_PROFILES = [
    "RC4_MD5_TLS10",
    "RC4_SHA",
    "DH_ANON_RC4",
    "ECDHE_ECDSA_RC4",
    "ECDHE_RSA_RC4",
    "DES3_RSA_TLS11",
    "DES3_DHE_DSS",
    "DES3_DHE_RSA",
    "DES3_ECDHE_ECDSA",
    "DES3_ECDHE_RSA",
    "CERT_MD5",
    "CERT_SHA1_RSA",
    "CERT_ECDSA_SHA1",
    "WEAK_CURVE",
    "RSA_AES128_SHA",
    "RSA_AES256_SHA",
    "RSA_AES128_SHA256",
    "RSA_AES256_SHA256",
]
def send_tcp_and_read(host: str, port: int, payload: bytes, timeout: float = 1.5) -> bytes:
    sock = socket.create_connection((host, port), timeout=3)
    sock.settimeout(timeout)
    try:
        if payload:
            sock.sendall(payload)
        chunks: list[bytes] = []
        while True:
            try:
                data = sock.recv(4096)
            except socket.timeout:
                break
            if not data:
                break
            chunks.append(data)
            if len(data) < 4096:
                break
        return b"".join(chunks)
    finally:
        sock.close()
def http_request(path: str, headers: list[tuple[str, str]] | None = None, body: bytes = b"", method: str = "GET") -> bytes:
    headers = headers or []
    lines = [f"{method} {path} HTTP/1.1", "Host: weaktls-lab.local", "Connection: close"]
    lower_names = {name.lower() for name, _ in headers}
    if body and "content-length" not in lower_names:
        headers.append(("Content-Length", str(len(body))))
    for name, value in headers:
        lines.append(f"{name}: {value}")
    return ("\r\n".join(lines) + "\r\n\r\n").encode("ascii") + body
def send_and_log(name: str, host: str, port: int, payload: bytes) -> None:
    response = send_tcp_and_read(host, port, payload)
    head = response[:16].hex() if response else "no-response"
    print(f"[client-send] {name} -> {host}:{port} {head}")
    time.sleep(0.15)
def send_tls_profiles(server_ip: str, tls_port: int) -> None:
    for profile in TLS_PROFILES:
        payload = f"PROFILE {profile}\r\n".encode("ascii")
        send_and_log(f"tls-{profile.lower()}", server_ip, tls_port, payload)
def send_starttls_flow(server_ip: str, starttls_port: int) -> None:
    sock = socket.create_connection((server_ip, starttls_port), timeout=3)
    sock.settimeout(1.5)
    try:
        banner = sock.recv(4096)
        print(f"[client-send] starttls-banner <- {banner.splitlines()[0].decode('latin-1', errors='ignore') if banner else 'none'}")
        sock.sendall(b"EHLO client.example\r\n")
        capability = sock.recv(4096)
        print(f"[client-send] starttls-capability <- {capability.decode('latin-1', errors='ignore').strip()}")
        sock.sendall(b"AUTH PLAIN dGVzdAB0ZXN0AHRlc3Q=\r\n")
        auth_reply = sock.recv(4096)
        print(f"[client-send] starttls-auth <- {auth_reply.decode('latin-1', errors='ignore').strip()}")
    finally:
        sock.close()
    time.sleep(0.15)
def send_http_leak(server_ip: str, http_port: int) -> None:
    payload = http_request(
        "/collect?debug=1",
        headers=[
            ("Authorization", "Basic dGVzdDp0ZXN0"),
            ("Cookie", "session=SESSIONTOKEN123456; auth=AUTHVALUE123456"),
        ],
    )
    send_and_log("http-clear-credential", server_ip, http_port, payload)
def send_mail_clear_auth(server_ip: str, starttls_port: int) -> None:
    sock = socket.create_connection((server_ip, starttls_port), timeout=3)
    sock.settimeout(1.5)
    try:
        _ = sock.recv(4096)
        sock.sendall(b"EHLO second.example\r\n")
        _ = sock.recv(4096)
        sock.sendall(b"LOGIN weakuser\r\n")
        reply = sock.recv(4096)
        print(f"[client-send] clear-mail-auth <- {reply.decode('latin-1', errors='ignore').strip()}")
    finally:
        sock.close()
    time.sleep(0.15)
def send_admin_port_probes(server_ip: str) -> None:
    ports = [88, 135, 139, 389, 445, 464, 636, 5985]
    for port in ports:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(0.3)
        try:
            sock.connect((server_ip, port))
        except OSError:
            pass
        finally:
            sock.close()
        print(f"[client-send] admin-probe -> {server_ip}:{port}")
        time.sleep(0.05)
def send_quic_probe(server_ip: str, quic_port: int) -> None:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(1.0)
    try:
        sock.sendto(b"\xC1\x01\x02\x03clienthello", (server_ip, quic_port))
        response, _ = sock.recvfrom(4096)
        print(f"[client-send] quic-probe <- {response.hex()}")
    finally:
        sock.close()
    time.sleep(0.15)
def main() -> None:
    parser = argparse.ArgumentParser(description="Send live weak TLS supplement traffic to 10.10.10.2")
    parser.add_argument("--server-ip", default="10.10.10.2")
    parser.add_argument("--tls-port", type=int, default=18443)
    parser.add_argument("--http-port", type=int, default=18080)
    parser.add_argument("--starttls-port", type=int, default=18125)
    parser.add_argument("--quic-port", type=int, default=18480)
    args = parser.parse_args()
    send_tls_profiles(args.server_ip, args.tls_port)
    send_starttls_flow(args.server_ip, args.starttls_port)
    send_http_leak(args.server_ip, args.http_port)
    send_mail_clear_auth(args.server_ip, args.starttls_port)
    send_admin_port_probes(args.server_ip)
    send_quic_probe(args.server_ip, args.quic_port)
if __name__ == "__main__":
    main()

 

posted @ 2026-03-30 17:34  岐岐卡卡西  阅读(2)  评论(0)    收藏  举报