feat: add port scanner module for camera network discovery
Implements a dedicated port scanning utility targeting camera IP ranges, enabling detection of open ports and active services on connected surveillance devices. The module includes TCP connect scanning with configurable timeout and port range parameters, outputting results in structured format for further processing.
This commit is contained in:
parent
0ef1ff62f7
commit
b7f843f8eb
319
portscan.py
Normal file
319
portscan.py
Normal file
@ -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())
|
||||
Loading…
Reference in New Issue
Block a user