pythonpython-3.xnmappython-3.11xmltocsv

Only the first open port number is saving to the csv file


Only the first open port in the <ports> </ports> tag is listed in the CSV output file; no other open ports are listed. The output is printed to the csv file but only the first line in the ports tag is output to the csv file. You can try by run the nmap command "nmap -sV -Pn -oX Output_3.xml scanme.nmap.org"

`import xml.etree.ElementTree as ET
import csv

def main():
    in_xml_port = 'Output_1.xml'
    xml_tree_port = ET.parse(in_xml_port)
    xml_root_port = xml_tree_port.getroot()
    
   # To save the output to csv file 
    with open('detected_hosts.csv', 'w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(['IP', 'Hostname', 'Status', 'Port', 'Service', 'Protocol', 'Product'])

        for host in xml_root_port.findall('host'):

            # finding all the necessary details from the xml tags
            master_ip = host.find('address').get('addr')
            
            master_hostnames = host.findall('hostnames')
            master_hostname = master_hostnames[0].findall('hostname')[0].attrib['name']
            
            port_status = host.findall('ports')
            port_status_1 = port_status[0].findall('port')
            port_state = port_status_1[0].findall('state')[0].attrib['state']
            
            port_element = host.findall('ports')
            port_id = port_element[0].findall('port')[0].attrib['portid']

            open_service = host.findall('ports')
            open_service_1 = open_service[0].findall('port')
            service_name = open_service_1[0].findall('service')[0].attrib['name']
            
            potocol_element = host.findall('ports')
            port_protocol = potocol_element[0].findall('port')[0].attrib['protocol']

            product_element = host.findall('ports')
            product_element_1 = product_element[0].findall('port')
            product_name = product_element_1[0].findall('service')[0].attrib['product']
       
            if port_state == 'open':
                host_status = 'open'
                writer.writerow([master_ip, master_hostname, port_state, port_id, service_name, port_protocol, product_name])

                
if __name__ == '__main__':
    main()`

Solution

  • The issue is that when searching for 'ports', the code only gets the first response. For instance, these two lines:

    port_status_1 = port_status[0].findall('port')
    port_state = port_status_1[0].findall('state')[0].attrib['state']
    

    port_status_1 is a list of ports, yet when querying for port_state, the code restricts itself to only the first port port_status_1[0]. Hence, in the generated CSV file, only the info of the first open port is recorded.

    Give the following script a try. The main logic is not changed, except that it uses a loop to get all 'port' from all 'ports'. In addition, it uses find() and get() to simplify the querying of an attribute.

    import xml.etree.ElementTree as ET
    import csv
    
    def main():
        in_xml_port = 'Output_1.xml'
        xml_tree_port = ET.parse(in_xml_port)
        xml_root_port = xml_tree_port.getroot()
        
       # To save the output to csv file 
        with open('detected_hosts.csv', 'w', newline='') as file:
            writer = csv.writer(file)
            writer.writerow(['IP', 'Hostname', 'Status', 'Port', 'Service', 'Protocol', 'Product'])
    
            for host in xml_root_port.findall('host'):
    
                # finding all the necessary details from the xml tags
                master_ip = host.find('address').get('addr')
                
                master_hostnames = host.findall('hostnames')
                master_hostname = master_hostnames[0].findall('hostname')[0].attrib['name']
    
                for ports in host.findall('ports'):
                    for port in ports.findall('port'):
                        port_state = port.find('state').get('state')
                        port_id = port.get('portid')
                        service_name = port.find('service').get('name')
                        port_protocol = port.get('protocol')
                        product_name = port.find('service').get('product')
                        if port_state == 'open':
                            writer.writerow([master_ip, master_hostname, port_state, port_id, service_name, port_protocol, product_name])
                    
    if __name__ == '__main__':
        main()