From c04de5a558d6ce26d229bee44445c67e9b9d49ae Mon Sep 17 00:00:00 2001 From: Laurent Ghigonis Date: Tue, 23 Jul 2013 05:57:00 +0200 Subject: autoscan: improve monitor mode --- autoscan/autoscan.py | 125 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/autoscan/autoscan.py b/autoscan/autoscan.py index 182b8ba..4736869 100755 --- a/autoscan/autoscan.py +++ b/autoscan/autoscan.py @@ -3,23 +3,6 @@ # autoscan - automatic fingerprint of visited networks # 2013, Laurent Ghigonis -# Usage: autoscan.py [-d] [interfaces] -# by default, monitor all network interfaces -# Should work on all Linux versions - -# Each time network connectivity become available after a cut-off, -# run some tests and store results in -# YYYYMMDD_hhmmss_interface/testname/output" -# * ifconfig -# * if WIFI, iwconfig -# * /etc/resolv.conf -# * 15s pcap -# * route -n -# * traceroute -# * local net IP scan -# * public IP (curl ifconfig.me) -# * ping 8.8.8.8 - import sys import os import time @@ -31,11 +14,12 @@ import shutil import errno class Autoscan_iface(object): - def __init__(self, iface, logpath=".", pubip="8.8.8.8", verbose=0): + def __init__(self, iface, logpath=".", target_pubip="8.8.8.8", verbose=0, noexplore=False): self.iface = iface self.logpath = logpath self.verbose = verbose - self.pubip = pubip + self.target_pubip = target_pubip + self.noexplore = noexplore self.date = None # set by _do_tests() if 'SUDO_UID' in os.environ and 'SUDO_GID' in os.environ: self.perm_uid = int(os.environ['SUDO_UID']) @@ -48,38 +32,70 @@ class Autoscan_iface(object): self.found_pubip = None self.found_dns = list() + def run_now(self): + self._do_tests() + def monitor(self): + self._wait_up() self._do_tests() while True: self._wait_down() self._wait_up() self._do_tests() - def run_now(self): - self._do_tests() - def _wait_up(self): + if self.verbose >= 1: + print("[>] _wait_up") while True: out, err, code = self._exec( ['ifconfig', self.iface]) + # iface up up = re.search(r'UP', out) ip4 = re.search(r'inet (\S+)', out) ip6 = re.search(r'inet6 (\S+)', out) - if up and (ip4 or ip6): + if up and ip4: # XXX no ip6 because too fast break - time.sleep(1) + # loop + time.sleep(0.5) + time.sleep(3) # XXX wait for network to be configured def _wait_down(self): + if self.verbose >= 1: + print("[>] _wait_down") + last_ip4 = None + last_ip6 = None + last_t = None while True: out, err, code = self._exec( ['ifconfig', self.iface]) + # iface down up = re.search(r'UP', out) if not up: break - # XXX also consider sleep as interface down - time.sleep(1) + # iface ip change + ip4 = re.search(r'inet (\S+)', out) + if ip4: ip4 = ip4.group(1) + if (not ip4 and last_ip4) or \ + (ip4 and last_ip4 and ip4 != last_ip4): + break + last_ip4 = ip4 + ip6 = re.search(r'inet6 (\S+)', out) + if ip6: ip6 = ip6.group(1) + if (not ip6 and last_ip6) or \ + (ip6 and last_ip6 and ip6 != last_ip6): + break + last_ip6 = ip6 + # sleep detection + t = time.clock() + if last_t and (t - last_t > 1): + break + last_t = t + # loop + time.sleep(0.5) def _do_tests(self): + if self.verbose >= 1: + print("[>] _do_tests") self.date = time.strftime("%Y%m%d_%H%M%S", time.gmtime()) self._do_tests_run(self._test_pcap) self._do_tests_run(self._test_ifconfig) @@ -90,16 +106,10 @@ class Autoscan_iface(object): self._do_tests_run(self._test_pubip_ping) self._do_tests_run(self._test_pubip_traceroute) self._do_tests_run(self._test_resolv_traceroute) - self._do_tests_run(self._test_explor_traceroute) - self._do_tests_run(self._test_explor_scan) - if self.found_pubip: - suffix = self.found_pubip - else: - suffix = self.found_ip4 - newpath = self._storepath_get() + "_" + suffix - if self.verbose >= 1: - print "[*] %s" % newpath - os.rename(self._storepath_get(), newpath) + if not self.noexplore: + self._do_tests_run(self._test_explor_traceroute) + self._do_tests_run(self._test_explor_scan) + self._storepath_rename() def _do_tests_run(self, func): try: @@ -167,35 +177,34 @@ class Autoscan_iface(object): def _test_pubip_get(self): out, err, code = self._exec( ['curl', '--retry', '3', 'ifconfig.me']) - self._store("pubip_get/ip", out) - self.found_pubip = out.strip() + if re.search(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', out): + self._store("pubip_get/ip", out) + self.found_pubip = out.strip() + else: + self._store("pubip_get/out", out) + self.found_pubip = None def _test_pubip_ping(self): out, err, code = self._exec( - ['ping', '-W', '3', '-c', '1', self.pubip]) + ['ping', '-W', '3', '-c', '1', self.target_pubip]) self._store("pubip_ping/code", code) self._store("pubip_ping/out", out) + def _test_pubip_traceroute(self): + self._store("pubip_traceroute/out", + self._util_traceroute(self.target_pubip)) + def _test_resolv_traceroute(self): for dns in self.found_dns: self._store("resolv_traceroute/out", self._util_traceroute(dns)) - def _test_pubip_traceroute(self): - self._store("pubip_traceroute/out", - self._util_traceroute(self.pubip)) - def _test_explor_traceroute(self): targets = ["192.168.0.1", "192.168.1.1", "192.168.2.1", "10.0.0.1", "172.16.0.1"] for t in targets: self._store("explor_traceroute/out_%s" % t, self._util_traceroute(t)) - def _util_traceroute(self, target): - out, err, code = self._exec( - ['traceroute', target]) - return out - def _test_explor_scan(self): target = re.sub('\.[0-9]+$', '', self.found_ip4) + "/24" # XXX v6 out, err, code = self._exec( @@ -229,6 +238,22 @@ class Autoscan_iface(object): subprocess.check_output(['chown', '-R', '%s:%s' % (self.perm_uid, self.perm_gid), self.logpath]) # pythonic way is awefull return path + def _storepath_rename(self): + if self.found_pubip: + suffix = self.found_pubip + else: + suffix = self.found_ip4 + newpath = self._storepath_get() + "_" + suffix + if self.verbose >= 1: + print "[*] %s" % newpath + os.rename(self._storepath_get(), newpath) + + def _util_traceroute(self, target): + out, err, code = self._exec( + ['traceroute', target]) + return out + + if not os.geteuid() == 0: sys.exit('must be root') @@ -245,6 +270,8 @@ parser.add_argument("-f", "--foreground", action="store_true", help="Run in foreground for monitor mode, do not daemonize") parser.add_argument("-o", "--outdir", action="store", default=".", help="Use DIR as output directory") +parser.add_argument("-x", "--noexplore", action="store_true", + help="Do not run explore tests (traceroute to arbitrary local ranges + nmap scan)") parser.add_argument("-p", "--pubip", action="store", default="8.8.8.8", help="Use target IP for public IP tests") parser.add_argument("-v", "--verbose", action="store_true", @@ -264,7 +291,7 @@ if not args.runnow and not args.monitor: for iface in args.interfaces: if os.fork() == 0: autoscan = Autoscan_iface(iface, args.outdir, args.pubip, - args.verbose) + args.verbose, args.noexplore) if args.runnow: autoscan.run_now() else: -- cgit v1.2.3-59-g8ed1b