def vcheck(recent_version_url: str) -> Union[str, bool, None]: """ compare given remote and local VERSION file return False if failed, None if no newer version found or recent version """ try: with open('VERSION') as file: current_version = file.read().strip() except: log.warn('failed to read file `VERSION`.') return False recent_version = get(recent_version_url) if not recent_version.status_code == 200: log.warn('failed to get recent version.') return False recent_version = recent_version.text.strip() for current, recent in zip(current_version.split('.'), recent_version.split('.')): if recent > current: log.notice('v{0} is available (current: v{1}).'.format( recent_version, current_version)) return recent_version else: log.debug('up to date: v{0}.'.format(current_version)) return None
def write(self, data: Union[list, dict], **kwargs) -> bool: """ save dict or list of dicts to json file (optionally sorted) data: data: dict or list of dicts kwargs: list_sort_key: str [default: None] reverse: bool [default: False] """ try: with open(self.filename, 'w+', encoding='utf-8') as f: f.write( json.dumps(self._sort(data, kwargs), indent=4, default=str)) log.debug('data exported to `{0}`.'.format(self.filename)) except FileNotFoundError: log.warn('`{0}` not found.'.format(self.filename)) self.error = True return False except PermissionError: log.warn('Could not write `{0}`, permission denied.'.format( self.filename)) self.error = True return False self.error = False return True
def crawl_net(subnet: str = None) -> Iterator[Union[str, str]]: """ scan given subnet for IPs and MACs using nmap """ log.notice("scanning {0} for machines".format(subnet)) cmd = "nmap -sn" cmd = cmd.split() cmd.append(subnet) stdout, _ = exe(cmd, timeout=300) if not stdout: log.warn("failed to scan network.") return None, None, None s = stdout.replace('\n', '|') for chunk in re.findall(r"Nmap scan report for.*?MAC Address.*?\(.*?\)", s): try: ip = re.findall(ip_pattern, chunk)[0] mac = re.findall(mac_pattern, chunk)[0] except IndexError: continue comment = re.findall(r"{0}\s\(.*?\)".format(mac), chunk) comment = '' if len(comment) == 0 else comment[0][19:-1] log.debug("found {0}, {1} ({2})".format(ip, mac, comment)) yield ip, mac, comment
def scan_net(subnet: str) -> None: """ crawl net and add found IPs """ for ip, mac, comment in crawl_net(subnet): new = registered(ip, True) if new: log.debug('found {1} ({0}, {2})'.format(ip, mac, comment)) if machine[ip].data['comment'] == '': machine[ip].data['comment'] = comment if not machine[ip].data['mac']: machine[ip].data['mac'] = mac
def exe(command: Union[list, str], **kwargs) -> Tuple[Union[str, bool], Union[str, Exception]]: """ execute external process, returns (stdout: str|false, stderr: str) kwargs: timeout: default = 300sec label: log-label """ timeout = kwargs.pop('timeout', 300) if not isinstance(timeout, int): raise ValueError("timeout can be int only") if isinstance(command, str): command = command.split() elif not isinstance(command, list): raise ValueError("command can be list or string only") label = kwargs.pop('label', command[0]) try: subp = Popen(command, stdout=PIPE, stderr=STDOUT) except OSError: log.crit("`{0}` not found".format(command[0])) return False, 'not found' except Exception as e: log.crit("`{0}` failed: {1}".format(command[0], e)) return False, e try: stdout, stderr = subp.communicate(timeout=timeout) except TimeoutExpired: log.err('`{0}` exceeded timeout ({1} sesc}.'.format( command[0], timeout)) subp.kill() return False, 'timeout' except Exception as e: log.crit("`{0}` communication failed: {1}".format(command[0], e)) return False, e stdout = '' if not stdout else stdout.decode('utf8') stderr = '' if not stderr else stderr.decode('utf8') log.debug('`{0}` executed.'.format(label)) return stdout, stderr
def openvas_run_task() -> None: """ try to start OV task until success, ordered by last_report """ if tasks_running() > config['run_limit']: log.info('limit of {0} running tasks reached, no new task.'.format( config['run_limit'])) return for ip, _ in test_order('last_attempt'): if machine[ip].running: log.info('[{0}]: task already running'.format( machine[ip].data['mac'])) continue machine[ip].data['last_attempt'] = now() if args.verify: real_mac = get_mac(ip) if real_mac and not machine[ip].data['mac'] == real_mac: log.warn('[{0}]: ERROR: address conflict {1} ({2})'.format( machine[ip].data['mac'], real_mac, ip)) continue if ov.run_task(machine[ip].task): log.notice('[{0}]: started OpenVAS task'.format( machine[ip].data['mac'])) sleep(60 * 2) if task_running(ip): log.debug('[{0}]: running task verified'.format( machine[ip].data['mac'])) machine[ip].keep = True break else: log.warn('[{0}]: task was aborted'.format( machine[ip].data['mac'])) continue else: log.warn('[{0}]: FAILED starting OpenVAS task'.format( machine[ip].data['mac'])) continue else: log.err('FAILED to start any OpenVAS task.')
def test_order(sort_key: str) -> Iterator[Tuple[str, str]]: """ return list (ip, sort_key) ordered by sort_key value """ r = [] for ip in machine: if not machine[ip].keep or machine[ip].skip: log.debug('excluded {0} from run-test-candidates.'.format(ip)) continue # TODO optional min duration between tests of single machines r.append((ip, machine[ip].data[sort_key])) r.sort(key=lambda k: k[1]) return r
def get_mac(ip: str) -> Union[str, None]: """ get current mac address from ip using arping """ cmd = "/usr/sbin/arping -c1 {0}".format(ip) cmd = cmd.split() stdout, _ = exe(cmd, timeout=300) if not stdout: log.crit("failed to get mac address") return None try: mac = re.findall(mac_pattern, stdout.upper())[0] log.debug("current mac for {0}: {1}".format(ip, mac)) return mac except IndexError: return None
def __init__(self, ip: str) -> None: self.keep = self.skip = self.running = False self.mac_new = None self.target = self.task = self.report = None self.data = { 'ip': ip, 'mac': None, 'comment': '', 'severity': -1, 'link': None, 'created': default_stamp, 'last_attempt': default_stamp, 'last_report': default_stamp, 'want_check': False, 'ip_changed': False } log.debug('created container for {0}'.format(ip))
def _sort(self, data: Union[list, dict], kwargs: dict) -> Union[list, dict]: """ sort data if list """ for key in ('list_sort_key', 'reverse'): if key in kwargs: setattr(self, key, kwargs.get(key)) if not self.list_sort_key or not isinstance(data, list): return data try: data = sorted(data, key=lambda k: k[self.list_sort_key], reverse=self.reverse) sorted_by = " reversed" if self.reverse else "" log.debug('sorted by `{0}`{1}.'.format(self.list_sort_key, sorted_by)) except: pass return data
def mk_machine(json_data: dict, source: str) -> None: """ create new machine object """ try: json_data['ip'], json_data['mac'] except KeyError: log.err('failed importing `{0}`'.format(source)) return ip = json_data['ip'] new = registered(ip, True) if not machine[ip].data['mac']: machine[ip].data['mac'] = json_data['mac'].upper() if json_data.get('skip', False): machine[ip].skip = machine[ip].data['skip'] = True machine[ip].data['comment'] = json_data['comment'] if new: log.debug('config for {0} [{1}] imported'.format( machine[ip].data['ip'], machine[ip].data['mac']))
def read(self, **kwargs) -> Union[list, dict, None]: """ load file content as dict or list of dicts (optionally sorted) kwargs: list_sort_key: str [default: None] reverse: bool [default: False] """ try: with open(self.filename) as json_file: data = json.load(json_file) log.debug('`{0}` imported.'.format(self.filename)) except FileNotFoundError: log.warn('`{0}` not found.'.format(self.filename)) self.error = True return None except PermissionError: log.warn('Could not read `{0}`, permission denied.'.format( self.filename)) self.error = True return None self.error = False return self._sort(data, kwargs)
severity, '\t\t', last_report, '\t\t', comment) print('{0} running tasks'.format(tasks_running())) if __name__ == '__main__': args = get_args() if args.vv: log.log_level = 7 elif args.v: log.log_level = 6 if args.print: log.use_tty = False log.debug('initiated...') vcheck( 'https://raw.githubusercontent.com/TMagerl/AutoOpenVAS/master/VERSION') config = load_config() ov = OpenVAS('admin', config['passwd'], config['openvas_ip'], config['openvas_omp_port'], config['openvas_web_port']) machine = {} load_previous_data() import_targets(config['job_source']) if args.scan: scan_net(args.scan) openvas_analysis()