GoVanguard/legion

View on GitHub
buildHugeNmapTest.py

Summary

Maintainability
A
1 hr
Test Coverage
import xml.etree.ElementTree as ET
from random import randint, choice, choices, sample
import datetime

def generate_nmap_xml(num_hosts=1000, base_subnet="172.16"):
    """
    Generate a full sample nmap XML file with session information included.

    Parameters:
    num_hosts (int): Number of hosts to generate.
    base_subnet (str): Base subnet for the hosts.

    Returns:
    str: XML content as a string.
    """
    # XML header
    xml_header = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nmaprun>
<?xml-stylesheet href="file:///C:/Program Files (x86)/Nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 7.94 scan initiated as: nmap -sV -oX -->'''

    # Create root element
    nmaprun = ET.Element("nmaprun", {
        "scanner": "nmap",
        "args": "nmap -sV -oX",
        "start": "123456789",
        "startstr": "Mon Nov 20 13:33:54 2023",
        "version": "7.94",
        "xmloutputversion": "1.05"
    })

    # Create scaninfo, verbose, and debugging elements
    scaninfo = ET.SubElement(nmaprun, "scaninfo", {
        "type": "syn",
        "protocol": "tcp",
        "numservices": "1000",
        "services": "1-65535"
    })
    ET.SubElement(nmaprun, "verbose", {"level": "0"})
    ET.SubElement(nmaprun, "debugging", {"level": "0"})

    # OS, services, and port mapping
    os_services = {
        "Linux": {"http": 80, "ssh": 22, "smtp": 25, "ftp": 21, "telnet": 23},
        "Windows": {"http": 80, "msrpc": 135, "netbios-ssn": 139, "rdp": 3389, "smb": 445},
        "Solaris": {"http": 80, "ssh": 22, "telnet": 23, "netbios-ssn": 139, "ftp": 21},
        "Darwin": {"http": 80, "netbios-ssn": 139, "ipp": 631, "afp": 548, "ssh": 22}
    }

    # Service products and versions
    service_info = {
        "http": {"product": "Apache httpd", "version": "2.4.41"},
        "ssh": {"product": "OpenSSH", "version": "8.0"},
        "smtp": {"product": "Postfix SMTP", "version": "3.4.8"},
        "ftp": {"product": "vsftpd", "version": "3.0.3"},
        "telnet": {"product": "Telnet Server", "version": "1.2"},
        "msrpc": {"product": "Microsoft RPC", "version": "5.0"},
        "netbios-ssn": {"product": "Samba smbd", "version": "4.10.10"},
        "rdp": {"product": "Microsoft Terminal Service", "version": "10.0"},
        "smb": {"product": "Microsoft SMB", "version": "3.1.1"},
        "ipp": {"product": "CUPS", "version": "2.3.0"},
        "afp": {"product": "Netatalk AFP", "version": "3.1.12"}
    }

    # Expanded lists for generating hostnames
    colors = ["Red", "Blue", "Green", "Yellow", "Purple", "Orange", "Cyan", "Magenta", "Lime", "Pink"]
    foods = ["Apple", "Burger", "Cake", "Dumpling", "Eclair", "Pizza", "Sushi", "Taco", "Waffle", "Bagel"]
    cities = ["Tokyo", "Paris", "London", "NewYork", "Sydney", "Berlin", "Rome", "Madrid", "Moscow", "Beijing"]
    verbs = ["Jumping", "Running", "Flying", "Swimming", "Dancing", "Singing", "Playing", "Walking", "Reading", "Writing"]

    # Unique hostname tracker
    generated_hostnames = set()

    # Function to create unique random hostnames
    def generate_hostname():
        while True:
            parts = [choice(colors), choice(foods), choice(cities), choice(verbs)]
            hostname = '.'.join(parts)
            # Ensure uniqueness by appending a number if needed
            if hostname not in generated_hostnames:
                generated_hostnames.add(hostname)
                return hostname
            else:
                hostname += str(randint(0, 9999))
                if hostname not in generated_hostnames:
                    generated_hostnames.add(hostname)
                    return hostname

    # Function to create a random IP address within the extended subnet range
    def random_ip(base_subnet, host_number):
        subnet_third_octet = host_number // 254
        host_fourth_octet = host_number % 254 + 1
        return f"{base_subnet}.{subnet_third_octet}.{host_fourth_octet}"

    # Generating hosts with updated IP address and hostname method
    for i in range(num_hosts):
        host_os = choice(list(os_services.keys()))

        host = ET.Element("host")
        ET.SubElement(host, "status", {"state": "up", "reason": "arp-response", "reason_ttl": "0"})
        ET.SubElement(host, "address", {"addr": random_ip(base_subnet, i), "addrtype": "ipv4"})

        # Hostnames
        hostnames = ET.SubElement(host, "hostnames")
        num_hostnames = randint(1, 3)  # Random number of hostnames per host
        for _ in range(num_hostnames):
            ET.SubElement(hostnames, "hostname", {"name": generate_hostname(), "type": "user"})

        # Ports
        ports = ET.SubElement(host, "ports")
        open_ports_count = randint(1, 5)  # Random number of open ports (1 to 5)
        services = sample(list(os_services[host_os].items()), open_ports_count)
        for service, port in services:
            port_element = ET.SubElement(ports, "port", {"protocol": "tcp", "portid": str(port)})
            ET.SubElement(port_element, "state", {"state": "open", "reason": "syn-ack", "reason_ttl": "64"})
            service_element = ET.SubElement(port_element, "service", {
                "name": service,
                "product": service_info[service]["product"],
                "version": service_info[service]["version"],
                "ostype": host_os
            })

        # OS element with osmatch
        os = ET.SubElement(host, "os")
        osclass = ET.SubElement(os, "osclass", {
            "type": "general purpose",
            "vendor": host_os,
            "osfamily": host_os,
            "osgen": "Unknown",  # OS generation can be set accordingly
            "accuracy": "98"
        })
        ET.SubElement(osclass, "cpe", text=f"cpe:/o:{host_os.lower()}:{host_os.lower()}")
        osmatch = ET.SubElement(os, "osmatch", {
            "name": f"{host_os} Generic Match",
            "accuracy": str(randint(90, 100)),
            "line": str(randint(1000, 2000))
        })
        ET.SubElement(osmatch, "osclass", {
            "type": "general purpose",
            "vendor": host_os,
            "osfamily": host_os,
            "osgen": "Unknown",
            "accuracy": "98"
        })

        nmaprun.append(host)

    # Runstats
    runstats = ET.SubElement(nmaprun, "runstats")
    ET.SubElement(runstats, "finished", {
        "time": "123456790",
        "timestr": "Mon Nov 20 13:34:10 2023",
        "summary": f"Nmap done; {num_hosts} IP addresses ({num_hosts} hosts up) scanned in 16.42 seconds",
        "elapsed": "16.42",
        "exit": "success"
    })
    ET.SubElement(runstats, "hosts", {
        "up": str(num_hosts),
        "down": "0",
        "total": str(num_hosts)
    })

    # Convert the XML to string
    xml_str = xml_header + '\n' + ET.tostring(nmaprun, encoding='unicode', method='xml')
    return xml_str

def save_nmap_xml(filename, num_hosts=200, base_subnet="172.16"):
    # Generate the XML content
    xml_content = generate_nmap_xml(num_hosts, base_subnet)

    # Save the content to the specified file
    with open(filename, "w") as file:
        file.write(xml_content)

# Specify the filename and call the function to save the file
filename = "huge_nmap.xml"
save_nmap_xml(filename)