diff --git a/portscan.py b/portscan.py new file mode 100644 index 0000000..9c6981f --- /dev/null +++ b/portscan.py @@ -0,0 +1,319 @@ +# Discovers if any common port is open, if so, it will scan for all ports on that host. + +import asyncio +import socket +import ipaddress +import platform +from tqdm import tqdm + +def get_local_ip(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(('8.8.8.8', 80)) + ip = s.getsockname()[0] + except Exception: + ip = '127.0.0.1' + finally: + s.close() + return ip + +async def ping(ip): + ping_cmd = f"ping -n 1 -w 1000 {ip}" if platform.system() == 'Windows' else f"ping -c 1 -W 1 {ip}" + try: + proc = await asyncio.create_subprocess_shell( + ping_cmd, + stdout=asyncio.subprocess.DEVNULL, + stderr=asyncio.subprocess.DEVNULL + ) + returncode = await proc.wait() + return returncode == 0 + except Exception: + return False + +async def check_port(ip, port, timeout=1): + try: + reader, writer = await asyncio.wait_for(asyncio.open_connection(ip, port), timeout) + writer.close() + await writer.wait_closed() + return True + except: + return False + +async def scan_ip(ip, ports, ports_services): + ip_str = str(ip) + if not await ping(ip_str): + return ip_str, False, [] + tasks = [check_port(ip_str, port) for port in ports] + results = await asyncio.gather(*tasks) + open_ports = [(port, ports_services[port]) for port, is_open in zip(ports, results) if is_open] + return ip_str, True, open_ports + +async def full_scan(ip, ports_services): + all_ports = range(1, 65536) + sem = asyncio.Semaphore(200) + with tqdm(total=len(all_ports), desc=f"Full scan {ip}") as pbar: + async def check(port): + async with sem: + if await check_port(ip, port, timeout=0.5): + service = ports_services.get(port, 'Unknown') + pbar.update(1) + return (port, service) + else: + pbar.update(1) + return None + tasks = [check(port) for port in all_ports] + results = await asyncio.gather(*tasks) + open_ports = sorted([p for p in results if p is not None]) + return ip, open_ports + +async def main(): + local_ip = get_local_ip() + network = ipaddress.IPv4Network(f"{local_ip}/24", strict=False) + ports_services = { + 1: 'TCPMUX', + 7: 'Echo', + 9: 'Discard', + 13: 'Daytime', + 17: 'QOTD', + 19: 'Chargen', + 20: 'FTP Data', + 21: 'FTP Control', + 22: 'SSH', + 23: 'Telnet', + 25: 'SMTP', + 37: 'Time', + 39: 'RLP', + 42: 'Nameserver', + 43: 'WHOIS', + 49: 'TACACS', + 53: 'DNS', + 67: 'BOOTPS', + 68: 'BOOTPC', + 69: 'TFTP', + 70: 'Gopher', + 79: 'Finger', + 80: 'HTTP', + 81: 'Hosts2-NS', + 88: 'Kerberos', + 102: 'ISO-TSAP', + 107: 'RTelnet', + 109: 'POP2', + 110: 'POP3', + 111: 'SunRPC', + 113: 'Ident', + 115: 'SFTP', + 119: 'NNTP', + 123: 'NTP', + 135: 'MS RPC', + 137: 'NetBIOS Name', + 138: 'NetBIOS Datagram', + 139: 'NetBIOS Session', + 143: 'IMAP', + 161: 'SNMP', + 162: 'SNMP Trap', + 179: 'BGP', + 194: 'IRC', + 201: 'AppleTalk Routing', + 209: 'QMTP', + 213: 'IPX', + 220: 'IMAPv3', + 389: 'LDAP', + 401: 'UPS', + 427: 'SLP', + 443: 'HTTPS', + 445: 'Microsoft DS', + 464: 'Kerberos Change', + 465: 'SMTPS', + 497: 'Dantz Retrospect', + 500: 'ISAKMP', + 512: 'Exec', + 513: 'Login', + 514: 'Syslog', + 515: 'LPD', + 520: 'RIP', + 543: 'Klogin', + 544: 'Kshell', + 546: 'DHCPv6 Client', + 547: 'DHCPv6 Server', + 554: 'RTSP', + 563: 'NNTPS', + 587: 'SMTP Submission', + 591: 'FileMaker', + 593: 'HTTP RPC', + 631: 'IPP', + 636: 'LDAPS', + 639: 'MSDP', + 646: 'LDP', + 691: 'MS Exchange', + 749: 'Kerberos Admin', + 873: 'rsync', + 990: 'FTPS', + 993: 'IMAPS', + 994: 'IRCS', + 995: 'POP3S', + 1080: 'SOCKS', + 1194: 'OpenVPN', + 1433: 'MSSQL', + 1434: 'MSSQL Monitor', + 1521: 'Oracle', + 1720: 'H.323', + 1723: 'PPTP', + 1741: 'CiscoWorks', + 1812: 'RADIUS', + 1813: 'RADIUS Acct', + 1985: 'HSRP', + 2000: 'Cisco SCCP', + 2049: 'NFS', + 2082: 'cPanel', + 2083: 'cPanel SSL', + 2086: 'WHM', + 2087: 'WHM SSL', + 2095: 'cPanel Webmail', + 2096: 'cPanel Webmail SSL', + 2100: 'Oracle XDB FTP', + 2222: 'DirectAdmin', + 2301: 'Compaq Insight', + 2381: 'HP Insight', + 2401: 'CVS', + 2433: 'MS SQL Analysis', + 2483: 'Oracle DB SSL', + 2484: 'Oracle DB SSL', + 2638: 'Sybase', + 2710: 'XBT Tracker', + 3128: 'Squid', + 3260: 'iSCSI', + 3268: 'AD Global Catalog', + 3269: 'AD Global Catalog SSL', + 3306: 'MySQL', + 3389: 'RDP', + 3478: 'STUN', + 3690: 'SVN', + 3784: 'Ventrilo', + 3785: 'Ventrilo', + 3899: 'Remote Web Workplace', + 4000: 'Diablo II', + 4321: 'BoBo', + 4664: 'Google Desktop', + 4899: 'Radmin', + 5000: 'UPnP', + 5001: 'Synology', + 5009: 'Airport Admin', + 5051: 'IDA Agent', + 5060: 'SIP', + 5190: 'AIM/ICQ', + 5222: 'XMPP', + 5269: 'XMPP SSL', + 5432: 'PostgreSQL', + 5500: 'VNC Hotline', + 5554: 'Sasser', + 5631: 'pcAnywhere Data', + 5632: 'pcAnywhere Master', + 5800: 'VNC HTTP', + 5900: 'VNC', + 6000: 'X11', + 6112: 'Battle.net', + 6129: 'DameWare', + 6346: 'Gnutella', + 6347: 'Gnutella', + 6379: 'Redis', + 6665: 'IRC', + 6666: 'IRC', + 6667: 'IRC', + 6668: 'IRC', + 6669: 'IRC', + 6697: 'IRC SSL', + 6881: 'BitTorrent', + 6969: 'BitTorrent', + 7212: 'GhostSite', + 7777: 'Oracle Web', + 8000: 'HTTP Alternate', + 8008: 'HTTP Alternate', + 8080: 'HTTP Alternate', + 8081: 'HTTP Alternate', + 8118: 'Privoxy', + 8200: 'MiniDLNA', + 8443: 'HTTPS Alternate', + 8554: 'RTSP Alternate', + 8767: 'TeamSpeak', + 8888: 'HTTP Alternate', + 9000: 'SonarQube', + 9001: 'Tor', + 9030: 'Tor', + 9040: 'Tor', + 9050: 'Tor', + 9100: 'JetDirect', + 9119: 'MXit', + 9200: 'Elasticsearch', + 9418: 'Git', + 9999: 'Abyss', + 10000: 'Webmin', + 11371: 'OpenPGP', + 12345: 'NetBus', + 13720: 'NetBackup', + 13721: 'NetBackup', + 13724: 'Veritas', + 13782: 'Veritas', + 13783: 'Veritas', + 19226: 'Panda', + 19638: 'Ensim', + 20000: 'Usermin', + 22222: 'EasyEngine', + 24800: 'Synergy', + 25999: 'Yahoo IM', + 27015: 'Half-Life', + 27017: 'MongoDB', + 27374: 'Sub7', + 28015: 'Rust', + 31337: 'Back Orifice', + 33434: 'Traceroute', + 37777: 'Dahua DVR', + 40193: 'Novell', + 41523: 'MS SQL', + 43594: 'Runescape', + 43595: 'Runescape', + 44818: 'EtherNet/IP', + 47808: 'BACnet', + 49152: 'Dynamic', + 49153: 'Dynamic', + 49154: 'Dynamic', + 49155: 'Dynamic', + 49156: 'Dynamic', + 49157: 'Dynamic', + 55555: 'FreeCiv', + 60000: 'Deep Throat', + 65000: 'Stacheldraht' + } + ports = list(ports_services.keys()) + + tasks = [scan_ip(ip, ports, ports_services) for ip in network.hosts()] + results = await asyncio.gather(*tasks) + + active_devices = [] + devices_need_full = [] + common_open_ports = {} + for ip, active, open_ports in results: + if active: + active_devices.append(ip) + if open_ports: + devices_need_full.append(ip) + common_open_ports[ip] = open_ports + + full_tasks = [full_scan(ip, ports_services) for ip in devices_need_full] + full_results = await asyncio.gather(*full_tasks) + + devices_with_open_ports = {ip: common_open_ports.get(ip, []) for ip in active_devices} + for full_ip, full_open in full_results: + devices_with_open_ports[full_ip] = full_open + + print("Active devices:") + for dev in active_devices: + print(dev) + + print("\nDevices with open ports:") + for ip, open_ports in devices_with_open_ports.items(): + if open_ports: + print(f"{ip}:") + for port, service in open_ports: + print(f" - {port}: {service}") + +asyncio.run(main())