python-3.xdictionarynestednmapport-scanning

pulling specific values from nested dictionary


i am currently writing an automation script using the nmap3 python library, but im stuck pulling values out of a nested dictionary, the idea is to pull all data related to the open ports found within the dictionary returned by the Nmap scans thank you any help would be appreciated

import nmap3
from sys import argv

class Scanner():
    def __init__(self, target_ip, output_name):
        self.target_ip = target_ip
        self.output_name = output_name

    #scan all TCP/UDP ports, then add to global list
    def initial_scan(self):
        nmap = nmap3.NmapScanTechniques()
        tcp_ports = []
        tcp_initial_scanner = nmap.nmap_tcp_scan(target=self.target_ip, args="-p- --min-rate 1500")
        for host, port_dict in tcp_initial_scanner.items():
            for port in port_dict[3]:
                tcp_ports.append(port['portid'])

        print(tcp_ports)

if __name__ == "__main__":
    target_ip = argv[1]
    output_name = argv[2]
    scanner = Scanner(target_ip, output_name)
    scanner.initial_scan()
# nmap scan results
{'10.10.10.5': {'hostname': [],
                'macaddress': None,
                'osmatch': {},
                'ports': [{'cpe': [],
                           'portid': '21',
                           'protocol': 'tcp',
                           'reason': 'syn-ack',
                           'reason_ttl': '127',
                           'scripts': [],
                           'service': {'conf': '3',
                                       'method': 'table',
                                       'name': 'ftp'},
                           'state': 'open'},
                          {'cpe': [],
                           'portid': '80',
                           'protocol': 'tcp',
                           'reason': 'syn-ack',
                           'reason_ttl': '127',
                           'scripts': [],
                           'service': {'conf': '3',
                                       'method': 'table',
                                       'name': 'http'},
                           'state': 'open'}],
                'state': {'reason': 'echo-reply',
                          'reason_ttl': '127',
                          'state': 'up'}},
 'runtime': {'elapsed': '0.28',
             'exit': 'success',
             'summary': 'Nmap done at Wed Apr 10 21:02:57 2024; 1 IP address '
                        '(1 host up) scanned in 0.28 seconds',
             'time': '1712797377',
             'timestr': 'Wed Apr 10 21:02:57 2024'},
 'stats': {'args': '/usr/bin/nmap -v -oX - --top-ports 10 -p21,80 --min-rate '
                   '1500 10.10.10.5',
           'scanner': 'nmap',
           'start': '1712797377',
           'startstr': 'Wed Apr 10 21:02:57 2024',
           'version': '7.94SVN',
           'xmloutputversion': '1.05'},
 'task_results': [{'extrainfo': '1 total hosts',
                   'task': 'Ping Scan',
                   'time': '1712797377'},
                  {'task': 'Parallel DNS resolution of 1 host.',
                   'time': '1712797377'},
                  {'extrainfo': '2 total ports',
                   'task': 'SYN Stealth Scan',
                   'time': '1712797377'}]}

I keep running into the following error

  File "/home/kali/Desktop/htb/devel/devel/scanner.py", line 15, in initial_scan
    for port in port_dict[3]:
                ~~~~~~~~~^^^
KeyError: 3

Solution

  • You need to do it this way: Add a check to ensure self.target_ip is present in the tcp_initial_scanner results before trying to access its details. The loop now iterates through ports_info and add default values for target_ip and output_name.

    import nmap3
    from sys import argv
    
    class Scanner():
        def __init__(self, target_ip, output_name):
            self.target_ip = target_ip
            self.output_name = output_name
    
        def initial_scan(self):
            nmap = nmap3.NmapScanTechniques()
            tcp_ports = []
            tcp_initial_scanner = nmap.nmap_tcp_scan(target=self.target_ip, args="-p- --min-rate 1500")
            if self.target_ip in tcp_initial_scanner:
                ports_info = tcp_initial_scanner[self.target_ip]['ports']
                for port_info in ports_info:
                    if port_info['state'] == 'open':
                        tcp_ports.append(port_info['portid'])
            else:
                print(f"No information for IP: {self.target_ip}")
    
            print(tcp_ports)
    
    if __name__ == "__main__":
        target_ip = argv[1] if len(argv) > 1 else '127.0.0.1' 
        output_name = argv[2] if len(argv) > 2 else 'output'   
        scanner = Scanner(target_ip, output_name)
        scanner.initial_scan()
    
    

    As I tested the solution in a Jupyter Notebook, here are the details of this and tha output:

    scan_results = {
        '10.10.10.5': {
            'hostname': [],
            'macaddress': None,
            'osmatch': {},
            'ports': [
                {
                    'cpe': [],
                    'portid': '21',
                    'protocol': 'tcp',
                    'reason': 'syn-ack',
                    'reason_ttl': '127',
                    'scripts': [],
                    'service': {
                        'conf': '3',
                        'method': 'table',
                        'name': 'ftp'
                    },
                    'state': 'open'
                },
                {
                    'cpe': [],
                    'portid': '80',
                    'protocol': 'tcp',
                    'reason': 'syn-ack',
                    'reason_ttl': '127',
                    'scripts': [],
                    'service': {
                        'conf': '3',
                        'method': 'table',
                        'name': 'http'
                    },
                    'state': 'open'
                }
            ],
            'state': {
                'reason': 'echo-reply',
                'reason_ttl': '127',
                'state': 'up'
            }
        },
        'runtime': {
            'elapsed': '0.28',
            'exit': 'success',
            'summary': 'Nmap done at Wed Apr 10 21:02:57 2024; 1 IP address (1 host up) scanned in 0.28 seconds',
            'time': '1712797377',
            'timestr': 'Wed Apr 10 21:02:57 2024'
        },
        'stats': {
            'args': '/usr/bin/nmap -v -oX - --top-ports 10 -p21,80 --min-rate 1500 10.10.10.5',
            'scanner': 'nmap',
            'start': '1712797377',
            'startstr': 'Wed Apr 10 21:02:57 2024',
            'version': '7.94SVN',
            'xmloutputversion': '1.05'
        },
        'task_results': [
            {
                'extrainfo': '1 total hosts',
                'task': 'Ping Scan',
                'time': '1712797377'
            },
            {
                'task': 'Parallel DNS resolution of 1 host.',
                'time': '1712797377'
            },
            {
                'extrainfo': '2 total ports',
                'task': 'SYN Stealth Scan',
                'time': '1712797377'
            }
        ]
    }
    
    def extract_open_ports(scan_results):
        open_ports = []
        for ip_address, details in scan_results.items():
            if 'ports' in details:
                for port in details['ports']:
                    if port['state'] == 'open':
                        open_ports.append((ip_address, port['portid'], port['service']['name']))
        return open_ports
    
    open_ports = extract_open_ports(scan_results)
    print("Open ports found:")
    for ip, port, service in open_ports:
        print(f"IP: {ip}, Port: {port}, Service: {service}")
    
    

    which retunrs:

    Open ports found:
    IP: 10.10.10.5, Port: 21, Service: ftp
    IP: 10.10.10.5, Port: 80, Service: http