From 41932c636291a344ca16db72fc2d79a1a6eb7afd Mon Sep 17 00:00:00 2001 From: Oleksii Shevchuk Date: Sun, 5 Mar 2017 15:44:52 +0200 Subject: [PATCH] Fix hosts ranges scan --- pupy/modules/port_scan.py | 51 ++++++++++++++++------------ pupy/network/lib/scan.py | 70 +++++++++++++++++++++++++++------------ 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/pupy/modules/port_scan.py b/pupy/modules/port_scan.py index 67c6101f..7bb880c3 100644 --- a/pupy/modules/port_scan.py +++ b/pupy/modules/port_scan.py @@ -25,11 +25,14 @@ class PortScan(PupyModule): self.arg_parser.add_argument('target', metavar="ip/range", help='IP/range') def run(self, args): - if "/" in args.target[0]: - hosts = IPNetwork(args.target[0]) + scan_range = False + + if '/' in args.target: + hosts = [ str(x) for x in IPNetwork(args.target) ] + scan_range = True + self.log('Scanning range {}: {} hosts'.format(args.target, len(hosts))) else: - hosts = list() - hosts.append(args.target) + hosts = [ args.target ] ports = [ p for prange in args.ports.split(',') for p in ( @@ -44,30 +47,36 @@ class PortScan(PupyModule): ports = list(set(ports)) random.shuffle(ports) - for host in hosts: - scanner = self.client.conn.modules['network.lib.scan'] + scanner = self.client.conn.modules['network.lib.scan'] - def set_connectable(ports): - self.connectable = ports - self.terminated.set() + def set_connectable(addrs): + self.connectable = addrs + self.terminated.set() - self.abort = scanner.scanthread( - str(host), ports, set_connectable, timeout=args.timeout, portion=args.portion - ) + self.connectable = [] - self.terminated.wait() + self.abort = scanner.scanthread( + hosts, ports, set_connectable, timeout=args.timeout, portion=args.portion + ) - ports = sorted(self.connectable) + self.terminated.wait() - if ports: - self.log('{}: {}'.format(host, ', '.join([str(x) for x in ports]))) - else: - self.log('{}: closed'.format(host)) + if self.connectable: + connectable = {} + for host, port in self.connectable: + if host in connectable: + connectable[host].add(port) + else: + connectable[host] = set([port]) - if self.abort.is_set(): - break + for host in sorted(connectable.keys()): + ports = ', '.join([str(port) for port in sorted(list(connectable[host]))]) + self.log('{}: {}'.format(host, ports)) - self.abort = None + elif not scan_range: + self.log('{}: closed'.format(args.target)) + + self.abort = None def interrupt(self): if self.abort: diff --git a/pupy/network/lib/scan.py b/pupy/network/lib/scan.py index 5a3a6381..3c41dbd4 100644 --- a/pupy/network/lib/scan.py +++ b/pupy/network/lib/scan.py @@ -6,20 +6,33 @@ import errno import time import threading import rpyc +import logging def chunks(l, n): - for i in xrange(0, len(l), n): - yield l[i:i + n] + chunk = [] + for i in l: + if len(chunk) == n: + yield chunk + chunk = [] + else: + chunk.append(i) + + if chunk: + yield chunk def create_socket(host, port): sock = socket.socket() sock.setblocking(0) - r = sock.connect_ex((host, port)) + try: + r = sock.connect_ex((host, port)) + except Exception, e: + return None, None + return sock, r -def scan(host, ports, abort=None, timeout=10, portion=32, on_complete=None): +def scan(hosts, ports, abort=None, timeout=10, portion=32, on_complete=None, on_open_port=None): connectable=[] - for portion in chunks(list(ports), portion): + for portion in chunks(((x, y) for x in hosts for y in ports), portion): if not portion: continue @@ -27,29 +40,44 @@ def scan(host, ports, abort=None, timeout=10, portion=32, on_complete=None): break sockets = {} - for port in portion: + for host, port in portion: sock, r = create_socket(host, port) + if sock is None: + continue + if r: if r in (errno.EAGAIN, errno.EINPROGRESS): - sockets[sock] = port + sockets[sock] = (host, port) else: sock.close() continue else: - connectable.append(port) + if on_open_port: + on_open_port((host, port)) + + connectable.append((host, port)) sock.close() - start = time.time() - while sockets and time.time() - start < timeout: - socks = list(sockets.iterkeys()) - _, w, _ = select.select([], socks, [], timeout - (time.time() - start)) + if sockets: + start = time.time() + while sockets and time.time() - start < timeout: + socks = list(sockets.iterkeys()) + _, w, _ = select.select([], socks, [], timeout - (time.time() - start)) - for sock in w: - errcode = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - if errcode == 0: - connectable.append(sockets[sock]) - sock.close() - del sockets[sock] + for sock in w: + try: + errcode = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if errcode == 0: + if on_open_port: + on_open_port(sockets[sock]) + + connectable.append(sockets[sock]) + except: + pass + + finally: + sock.close() + del sockets[sock] if on_complete: if abort and not abort.is_set(): @@ -57,8 +85,8 @@ def scan(host, ports, abort=None, timeout=10, portion=32, on_complete=None): else: return connectable -def scanthread(host, ports, on_complete, **kwargs): - host = str(host) +def scanthread(hosts, ports, on_complete, **kwargs): + hosts = [ x for x in hosts ] ports = [ x for x in ports ] abort = threading.Event() connectable = [] @@ -66,7 +94,7 @@ def scanthread(host, ports, on_complete, **kwargs): 'abort': abort, 'on_complete': rpyc.async(on_complete) }) - scanner = threading.Thread(target=scan, args=(host, ports), kwargs=kwargs) + scanner = threading.Thread(target=scan, args=(hosts, ports), kwargs=kwargs) scanner.daemon = True scanner.start()