Exemple #1
0
    def get(self, path):
        try:
            context = g.context
            connection = context.get_connection()

            file = File()
            result = file.read_file(context, path)

            if result == None:
                return make_response(
                    {
                        "status": "Err",
                        "error": f"File not found: {path}"
                    }, 404)

            response = make_response(result['file'])
            response.headers.set('Content-Type', result['mime_type'])
            #response.headers.set('Content-Disposition', 'attachment', filename='test.jpg')
            response.headers.set('Content-Disposition',
                                 'inline',
                                 filename=result['name'])
            return response

        except Exception as err:
            logger.exception(f"Exception: {err}")
            return make_response({"status": "Err", "error": str(err)}, 500)
Exemple #2
0
class HTML:
    """ class for HTML reporting """
    def __init__(self, date, opts, template_dir, report_dir, logs_dir):
        """ constructor """

        self.date = date
        self.opts = opts
        self.res = {}  # holds the results out of log dirs

        self.template_dir = template_dir
        self.report_dir = report_dir
        self.logs_dir = logs_dir

        self.file = File()

        self.file.copy_dirs(self.template_dir, self.report_dir)
        self.index_html = f'{self.report_dir}/index.html'
        self.res_html = f'{self.report_dir}/results.html'
        self.index_html_data = self.file.read_file(self.index_html)
        self.res_html_data = self.file.read_file(self.res_html)

        return

    def make_results(self):
        """ get all results from the targets log directory """

        logs = f'{self.logs_dir}/targets/'

        targets = os.listdir(logs)
        for target in targets:
            target_orig = target
            if ':' in target:
                target = target.replace(':', '-')
            self.res[target] = {}
            for moddir in os.listdir(logs + target_orig):
                if moddir in ('tcp', 'udp', 'social'):
                    submods = os.listdir(f'{logs}{target_orig}/{moddir}')
                    for s in submods:
                        for modname in os.listdir(
                                f'{logs}{target_orig}/{moddir}/{s}'):
                            if moddir == 'tcp' or moddir == 'udp':
                                module = f'{moddir}.{s}.{modname}'
                            else:
                                module = f'{moddir}.{modname}'
                            self.res[target][module] = {}
                            tool_path = f'{logs}{target_orig}/{moddir}/{s}/{modname}/'
                            for tool in os.listdir(tool_path):
                                if '.log' in tool:
                                    self.res[target][module][tool] = \
                                      '\n'.join(self.file.read_file(tool_path + tool))
                else:
                    for modname in os.listdir(f'{logs}{target_orig}/{moddir}'):
                        target = target.replace(':', '-')  # url port
                        module = f'{moddir}.{modname}'
                        self.res[target][module] = {}
                        for tool in os.listdir(
                                f'{logs}{target_orig}/{moddir}/{modname}'):
                            if '.log' in tool and '.bin' not in tool:
                                tool_path = f'{logs}{target_orig}/{moddir}/{modname}/'
                                self.res[target][module][tool] = '\n'.join(
                                    self.file.read_file(tool_path + tool))

        return

    def make_target_html(self):
        """ create $target.html with its contents/results """

        _mods = ''
        panels = []
        panel = '''
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title">$TOOL</h3>
      </div>
      <div class="panel-body">
        <pre>$RESULT</pre>
      </div>
    </div>
    '''

        for target, mods in self.res.items():
            t_html = f'{self.report_dir}/{target}.html'
            self.file.copy_files(self.res_html, t_html)
            t_html_data = self.file.read_file(t_html)
            t_html_data = [w.replace('$TARGET', target) for w in t_html_data]
            self.file.write_file(t_html, ' '.join(t_html_data))
            for mod, tools in mods.items():
                _mods += f'<h5>{mod}</h5>'
                for tool, data in tools.items():
                    tlink = tool.split('.log')[0]
                    link = f'{mod}.{tlink}'
                    _mods += f'<a href="#{link}" target="_blank">{tlink}</a><br />'
                    p1 = panel.replace(f'$TOOL',
                                       f'<a name="{link}">{link}</a>')
                    p2 = p1.replace('$RESULT', html.escape(data))
                    panels.append(p2)
            t_html_data = [w.replace('$ALLMODS', _mods) for w in t_html_data]
            _mods = ''
            t_html_data = [w.replace('$CONTENT', ' '.join(panels)) \
              for w in t_html_data]
            panels = []

            self.file.write_file(t_html, ' '.join(t_html_data))

        return

    def make_index_html(self):
        """ create the index.html """

        num_social_targets = 0
        patterns = {
            '$DATE': self.date,
            '$CMDLINE_ARGS': ' '.join(self.opts['cmdline'])
        }

        # index.html: date + cmdline args
        for k, v in patterns.items():
            self.index_html_data = [
                w.replace(k, v) for w in self.index_html_data
            ]

        # index.html: num target modes
        for k, v in self.opts['targets'].items():
            if k == 'social':
                for s in self.opts['targets'][k]:
                    if self.opts['targets'][k][s]:
                        num_social_targets += len(self.opts['targets'][k][s])
                self.index_html_data = [
                    w.replace('$NUM_SOCIAL', str(num_social_targets))
                    for w in self.index_html_data
                ]
            self.index_html_data = [w.replace(f'$NUM_{k.upper()}', str(len(v))) \
              for w in self.index_html_data]

        return

    def make_menu(self, mod, targets):
        """ create the menu and links """

        # targets menu items
        if mod == 'tcp' or mod == 'udp':
            l = []
            for target in self.opts['targets'][mod]:
                target = f"<li><a href='{target['host']}.html'>{target['host']}</a></li>"
                l.append(target)
            self.index_html_data = [
              w.replace(f'$TARGETS_{mod.upper()}', ' '.join(l)) \
              for w in self.index_html_data
            ]
            self.res_html_data = [
              w.replace(f'$TARGETS_{mod.upper()}', ' '.join(l)) \
              for w in self.res_html_data
            ]
        elif mod == 'social':
            l = []
            for part in self.opts['targets'][mod]:
                for target in self.opts['targets'][mod][part]:
                    target = f'<li><a href="{target}.html">{target}</a></li>'
                    l.append(target)
            self.index_html_data = [
              w.replace(f'$TARGETS_{mod.upper()}', ' '.join(l)) \
              for w in self.index_html_data
            ]
            self.res_html_data = [
              w.replace(f'$TARGETS_{mod.upper()}', ' '.join(l)) \
              for w in self.res_html_data
            ]
        else:
            for target in targets:
                if '://' in target:
                    target = target.split('://')[1].rstrip('/').split('/')[0]
                if ':' in target:
                    target = target.replace(':', '-')  # port
                target = f'<li><a href="{target}.html">{target}</a></li>'
                self.index_html_data = [
                  w.replace(f'$TARGETS_{mod.upper()}', target) \
                  for w in self.index_html_data
                ]
                self.res_html_data = [
                  w.replace(f'$TARGETS_{mod.upper()}', target) \
                  for w in self.res_html_data
                ]

        return

    def make_report(self):
        """ make the report """

        self.make_results()

        for mod, targets in self.opts['targets'].items():
            self.make_menu(mod, targets)
        self.make_index_html()

        self.file.write_file(self.index_html, ' '.join(self.index_html_data))
        self.file.write_file(self.res_html, ' '.join(self.res_html_data))

        self.make_target_html()
        self.file.del_file(f'{self.report_dir}/results.html')

        return
Exemple #3
0
class Controller:
    """ program controller class """
    def __init__(self):
        """ constructor """

        # options and modules
        self.opt = None
        self.mod = Module(MOD_PATH)

        # logger
        self.logger = Logger()
        self.log = self.logger.log

        # rest we need
        self.file = File()
        self.check = Check()
        self.misc = Misc()
        self.parser = None

        # nullscan working dir
        self.nullscan_dir = None

        return

    def prepare(self):
        """ preparation/initialization of opts and env: parsing & checks """

        # declare nullscan options
        self.opt = Option(sys.argv)

        # check argc and argc (usage)
        self.check.check_argc(len(sys.argv))
        self.check.check_argv(sys.argv)

        # check for missing libraries / deps / python modules
        self.check.check_deps(self.file.read_file(PYDEPS))

        # parse cmdline and config options, update final options dictionary
        try:
            self.parser = Parser(self.opt.opts)
            self.parser.parse_cmdline()
            self.parser.parse_config()
            self.opt.opts = self.parser.opts
        except:
            self.log('usage', _type='err', end='\n')

        # update final options dictionary
        self.opt.update_opts()

        # further checks for usage, options, env, etc.
        self.check.check_opts(self.opt.opts)

        # collect all py-files and grep the tools out of the py-files
        tools = []
        py_files = self.misc.find_py_files(MOD_PATH)
        for py in py_files:
            tools.append(self.misc.grep_tools(py))
        tools = [x for sublist in tools for x in sublist]

        # create the locks for each tool except for excluded ones
        with ThreadPoolExecutor(50) as exe:
            for tool in tools:
                if tool not in self.opt.opts['tools']['ex_tools']:
                    exe.submit(self.file.create_lock, tool)

        # copy debug flag to target_opts (for nullscan tools)
        self.opt.opts['targets_opts']['debug'] = self.opt.opts['debug']

        return

    def run_misc(self):
        """ run chosen misc options """

        if self.opt.opts['check_tools']:
            self.log('Checking for missing tools\n\n', _type='msg')
            self.check.check_tools()
            os._exit(SUCCESS)
        if self.opt.opts['print_tools']:
            self.log('Printing tools and information\n\n', _type='msg')
            self.misc.print_tool(self.opt.opts['print_tools'])
            os._exit(SUCCESS)
        if self.opt.opts['add_module']:
            self.log('Adding new module\n', _type='msg')
            self.misc.add_mod_tool('mod', self.opt.opts['add_module'])
            os._exit(SUCCESS)
        if self.opt.opts['add_tool']:
            self.log('Adding new tool\n', _type='msg')
            self.misc.add_mod_tool('tool', self.opt.opts['add_tool'])
            os._exit(SUCCESS)

        return

    def prepare_modules(self):
        """ filter in-/excluded mods + return a unique list """

        self.mod.filter_modules(self.opt.opts)
        self.mod.mods = list(set(self.mod.mods))

        return

    def run_nmap_mode(self):
        """ run nmap scan mode """

        nmap = Nmap(self.opt.opts['targets']['nmap'])

        # create nmap logdir based on scan type requested
        logpath = f'{self.nullscan_dir}/logs/nmap/{nmap.get_protocol()}'
        logfile = f'{logpath}/results'
        self.file.make_dir(logpath)
        nmap.set_logfile(logfile)

        # build nmap command line
        nmap.build_cmd()

        # start scans
        self.log('NMAP mode activated\n', _type='msg', color='blue')
        self.log('Targets added: {}\n'.format(
            len(self.opt.opts['targets']['nmap']['hosts'])),
                 _type='msg')
        if self.opt.opts['verbose']:
            self.log('\n')
            for target in self.opt.opts['targets']['nmap']['hosts']:
                self.log(f'{target}\n', _type='msg')
        nmap.scan(debug=self.opt.opts['debug'])

        return f'{logfile}.xml'

    def run_social_mode(self, target):
        """ run social mode """

        if '.social.' in str(self.mod.mods):
            # availabel social modules to import and load
            mods = [i for i in self.mod.mods if '.social.' in i]

            with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
                for key in target.keys():
                    if target[key]:
                        for t in target[key]:
                            # default module
                            rdir = f'{self.nullscan_dir}/logs/targets/'
                            wdir = f"{rdir}{t}/social/{key}/default"
                            exe.submit(self.mod.run_module,
                                       'modules.social.default', t,
                                       self.opt.opts, wdir)

                            # non-default modules
                            for m in mods:
                                if 'default' not in m:
                                    splitted = m.split('.')
                                    moddir = splitted[1]
                                    modname = splitted[2]
                                    wdir = f"{rdir}{t}/{moddir}/{key}/{modname}"
                                    exe.submit(self.mod.run_module, m, t,
                                               self.opt.opts, wdir)

        return

    def run_wifi_mode(self, target):
        """ run wifi mode """

        if '.wifi.' in str(self.mod.mods):
            # available wifi modules to import and load
            mods = [i for i in self.mod.mods if '.wifi.' in i]

            # default module first
            rdir = f'{self.nullscan_dir}/logs/targets/'
            wdir = f'{rdir}{target}/wifi/default'
            self.mod.run_module('modules.wifi.default', target, self.opt.opts,
                                wdir)

            # non-default modules
            with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
                for m in mods:
                    if 'default' not in m:
                        splitted = m.split('.')
                        moddir = splitted[1]
                        modname = splitted[2]
                        wdir = f'{rdir}{target}/{moddir}/{modname}'
                        exe.submit(self.mod.run_module, m, target,
                                   self.opt.opts, wdir)

        return

    def run_lan_mode(self, target):
        """ run lan mode """

        if '.lan.' in str(self.mod.mods):
            # available lan modules to import and load
            mods = [i for i in self.mod.mods if '.lan.' in i]

            # default module first
            rdir = f'{self.nullscan_dir}/logs/targets/'
            wdir = f'{rdir}{target}/lan/default'
            self.mod.run_module('modules.lan.default', target, self.opt.opts,
                                wdir)

            # non-default modules
            with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
                for m in mods:
                    if 'default' not in m:
                        splitted = m.split('.')
                        moddir = splitted[1]
                        modname = splitted[2]
                        wdir = f'{rdir}{target}/{moddir}/{modname}'
                        exe.submit(self.mod.run_module, m, target,
                                   self.opt.opts, wdir)

        return

    def run_web_mode(self, target):
        """ run web mode """

        if '.web.' in str(self.mod.mods):
            # available web modules to import and load
            mods = [i for i in self.mod.mods if '.web.' in i]

            # we need host name for working directory
            host = requests.utils.urlparse(target).netloc

            # default module first
            rdir = f'{self.nullscan_dir}/logs/targets/'
            wdir = f'{rdir}{host}/web/default'
            self.mod.run_module('modules.web.default', target, self.opt.opts,
                                wdir)

            # non-default modules
            with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
                for m in mods:
                    if 'default' not in m:
                        splitted = m.split('.')
                        moddir = splitted[1]
                        modname = splitted[2]
                        wdir = f'{rdir}{host}/{moddir}/{modname}'
                        exe.submit(self.mod.run_module, m, target,
                                   self.opt.opts, wdir)

        return

    def run_udp_mode(self, target):
        """ run udp mode """

        # we need to run host modules before tcp and we need to run default
        # tool first
        self.run_host_mode(target)

        # now tcp modules
        if '.udp.' in str(self.mod.mods):
            with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
                for p in target['ports']:
                    exe.submit(self.run_tcp_udp_mode, target, p, 'udp')

        return

    def run_tcp_mode(self, target):
        """ run tcp mode """

        # we need to run host modules before tcp and we need to run default
        # module first
        self.run_host_mode(target)

        # now tcp modules
        if '.tcp.' in str(self.mod.mods):
            with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
                for p in target['ports']:
                    exe.submit(self.run_tcp_udp_mode, target, p, 'tcp')

        return

    def run_tcp_udp_mode(self, host, port, proto):
        """ wrapper for tcp/udp mode """

        # available modules
        mod = f'modules.{proto}.{port[1]}'

        # force default module for given port if module does not exist
        if mod not in self.mod.mods:
            mod = f'modules.{proto}.default'

        # new target dict as we only need the corresponding port
        t = {'host': host['host'], 'port': port[0]}

        # default module first
        rdir = f'{self.nullscan_dir}/logs/targets/'
        wdir = f"{rdir}{t['host']}/{proto}/{port[0]}/default"
        self.mod.run_module(f'modules.{proto}.default', t, self.opt.opts, wdir)

        # now non-default module
        if '.default' not in mod:
            wdir = f"{rdir}{t['host']}/{proto}/{port[0]}/{port[1]}"
            self.mod.run_module(mod, t, self.opt.opts, wdir)

        return

    def run_host_mode(self, target):
        """ run host mode """

        # available host modules to import and load
        mods = [i for i in self.mod.mods if '.host.' in i]

        # default module
        rdir = f'{self.nullscan_dir}/logs/targets/'
        wdir = f"{rdir}{target['host']}/host/default"
        self.mod.run_module('modules.host.default', target, self.opt.opts,
                            wdir)

        # non-default modules
        with ProcessPoolExecutor(self.opt.opts['m_workers']) as exe:
            for m in mods:
                if 'default' not in m:
                    splitted = m.split('.')
                    moddir = splitted[1]
                    modname = splitted[2]
                    wdir = f"{rdir}{target['host']}/{moddir}/{modname}"
                    exe.submit(self.mod.run_module, m, target, self.opt.opts,
                               wdir)

        return

    def run_modes(self):
        """ run chosen modes """

        scans = []
        modes = {
            'tcp': self.run_tcp_mode,
            'udp': self.run_udp_mode,
            'wifi': self.run_wifi_mode,
            'web': self.run_web_mode,
            'social': self.run_social_mode,
            'http': self.run_web_mode,
            'https': self.run_web_mode,
        }

        # run lan mode first if requested
        if self.opt.opts['targets']['lan']:
            ifaces = []
            self.log('LAN mode activated\n', color='blue', _type='msg')
            self.log(
                f"Targets added: {len(self.opt.opts['targets']['lan'])}\n\n",
                _type='msg')
            for iface in self.opt.opts['targets']['lan']:
                if self.opt.opts['verbose']:
                    self.log(f'{iface}\n', _type='vmsg')
                ifaces.append((self.run_lan_mode, iface))
            if self.opt.opts['verbose']:
                self.log('\n')
            with ProcessPoolExecutor(self.opt.opts['t_workers']) as exe:
                self.log('Shooting tools\n\n', color='green', _type='msg')
                for iface in ifaces:
                    exe.submit(iface[0], iface[1])
            self.log('\n')
            if not self.opt.opts['verbose']:
                self.log('\n')
            self.log('Tools done\n\n', color='green', _type='msg')
            for log in glob.glob('**/lan/portscan/*.xml', recursive=True):
                if log:
                    self.parser.parse_nmap_logfile(log, lan=True)

        # collect scan modes for each target
        for k, v in self.opt.opts['targets'].items():
            if self.opt.opts['targets'][k]:
                if k == 'lan':
                    continue
                else:
                    self.log(f'{k.upper()} mode activated\n',
                             color='blue',
                             _type='msg')
                self.log(f'Targets added: {len(v)}\n\n', _type='msg')
                if k == 'social':
                    if self.opt.opts['verbose']:
                        for targets in v.values():
                            for target in targets:
                                self.log(f'{target}\n', _type='vmsg')
                        self.log('\n')
                    scans.append((modes[k], v))
                else:
                    for target in v:
                        scans.append((modes[k], target))
                        if self.opt.opts['verbose']:
                            if 'host' in target:
                                target_head = target['host']
                            else:
                                target_head = target
                            self.log(f'{target_head}\n', _type='vmsg')
                    if self.opt.opts['verbose']:
                        self.log('\n')

        # start mode for each target
        with ProcessPoolExecutor(self.opt.opts['t_workers']) as exe:
            if scans:
                self.log('Shooting tools\n\n', color='green', _type='msg')
                for scan in scans:
                    exe.submit(scan[0], scan[1])
        if scans:
            self.log('\n')
            if not self.opt.opts['verbose']:
                self.log('\n')
            self.log('Tools done\n\n', _type='msg', color='green')

        return

    def start(self):
        """ nullscan starts here with actions """

        self.check.check_uid()
        self.log('Game Started\n\n', _type='msg')

        # create nullscan working, targets and log dir
        self.nullscan_dir = self.file.make_dir(self.opt.opts['nullscan_dir'],
                                               incr=True)
        self.file.make_dir(f'{self.nullscan_dir}/logs/targets')
        self.opt.opts['targets_opts']['nullscan_logdir'] = \
          f'{self.nullscan_dir}/logs/targets/'

        # run nmap mode first if requested
        if 'hosts' in self.opt.opts['targets']['nmap']:
            self.parser.parse_nmap_logfile(self.run_nmap_mode())
            self.log('\n')

        # delete nmap key
        del self.opt.opts['targets']['nmap']

        # prepare modules for other modes
        self.prepare_modules()

        # run the nullscan modes now
        self.run_modes()

        return

    def end(self):
        """ program ends here. clean-ups, reporting, etc. """

        # go back to root dir
        os.chdir(ROOT_PATH)

        # clean up empty directories and empty (log-)files or logfiles containing
        # a singl ebyte (newline) (failed tools)
        self.misc.remove_empty_files_dirs(f'{self.nullscan_dir}/logs/targets/')

        # just to be safe - delete all locks, in case tools didn't
        self.file.del_file('/tmp/nullscan', _dir=True)

        # create report
        if self.opt.opts['report']:
            self.log('Creating report\n', _type='msg')
            tmpl_dir = f'{ROOT_PATH}/src/report/template'
            rep_dir = f'{self.nullscan_dir}/report'
            logs_dir = f'{self.nullscan_dir}/logs'
            self.report = HTML(TODAY, self.opt.opts, tmpl_dir, rep_dir,
                               logs_dir)
            self.report.make_report()
            self.log('Report done\n\n', _type='msg')
        self.log('Game Over\n', _type='msg')

        # reset terminal to original state. sometimes f**k up occurs because of
        # color and other escape codes.
        self.misc.reset_terminal()

        return
Exemple #4
0
class Parser:
    """ class for parsing cmdline and config file from nullscan """
    def __init__(self, opts):
        """ constructor"""

        self.opts = opts  # all nullscan options
        self.logger = Logger()
        self.log = self.logger.log
        self.file = File()
        self.misc = Misc()

        # tmp stupid fix to strip bad chars out of options
        self.bad_chars = '<>()[]{}:;.,="\'*&^%$#@!+-_/?°²³´`'

        return

    def parse_config(self):
        """ parse nullscan config file and get options+values as dictionary+list. """

        try:
            cfg = ConfigParser()
            data = cfg.read(self.opts['config']['file'])
            if not data:
                raise Exception
            for s in cfg.sections():
                for o in cfg.options(s):
                    values = cfg.get(s, o).replace('$NULLSCAN_DIR/', ROOT_PATH)
                    if ',' in values and o != 'nmap':
                        self.opts['config']['opts'][o] = values.split(',')
                    else:
                        self.opts['config']['opts'][o] = values
        except:
            self.log('config',
                     eargs=f"{self.opts['config']['file']}",
                     _type='err',
                     end='\n')

        return

    def parse_add_module_tool(self, args, dest):
        """ parse add module/tool option (-m/-a) """

        s = args.split()

        try:
            m = s[0].split('/')
            d = {
                'moddir': m[0],
                'modname': m[1],
                'func': s[1],
                'tool': s[2],
                'args': s[3:]
            }
        except:
            self.log('add_mod_tool', _type='err', end='\n')

        self.opts[dest] = d

        return

    def parse_print_tools(self, args):
        """ parse print tools option (-p) """

        dicted = {}
        splitted = list(filter(None, args.split(';')))

        for item in splitted:
            if item == 'all':
                dicted['all'] = True
                break
            if '=' in item:
                i = item.split('=')
                for j in i:
                    if ',' in j:
                        dicted[i[0]] = j.split(',')
                    else:
                        dicted[i[0]] = [j]
            else:
                dicted[item] = []

        self.opts['print_tools'] = dicted

        return

    def parse_tools(self, args, dest):
        """ parse tools include/exclude option (-I/-X) """

        self.opts['tools'][dest] = args.split(',')

        return

    def parse_modules(self, args, dest):
        """ parse modules include/exclude options (-i/-x) """

        # remove empty ones, if ';' was used at the end without further opts
        if ';' in args:
            mods = [x.strip(self.bad_chars) for x in args.split(';') if x]
        elif '=' in args:
            mods = args.split()
        else:
            self.log('inexmods', _type='err', end='\n')

        # create dict out of list itmes
        dicted = dict(d.split('=') for d in mods)

        for key, val in dicted.items():
            if ',' in val:
                dicted[key] = val.split(',')
            else:
                dicted[key] = [val]

        self.opts['modules'][dest] = dicted

        return

    def parse_extra_opts(self, args):
        """ parse extra options option (-o) """

        # remove empty ones, if ';' was used at the end without further opts
        if ';' in args:
            args = [x.strip(self.bad_chars) for x in args.split(';') if x]
        else:
            args = [args]

        # create dict out of list items except for nmap
        dicted = dict(d.split('=') for d in args)
        for key in dicted.keys():
            if ',' in dicted[key] and key != 'nmap':
                dicted[key] = dicted[key].split(',')

        self.opts['targets_opts'] = dicted

        return

    def parse_nmap_targets(self, targets):
        """ parse nmap targets to scan from either ranges or from file (-t) """

        hosts = []

        if os.path.isfile(targets):
            hosts = self.file.read_file(targets)
        else:
            try:
                # host range format
                if '-' in targets:
                    splitted = targets.split('-')
                    try:
                        start = ipaddress.IPv4Address(splitted[0])
                        end = ipaddress.IPv4Address(splitted[1])
                        for i in range(int(start), int(end) + 1):
                            ipaddr = str(ipaddress.IPv4Address(i))
                            hosts.append(ipaddr)
                    except:
                        # must be a hostname/domain then
                        if ',' in targets:
                            hosts = targets.split(',')
                        else:
                            hosts.append(targets)
                # cidr range format
                elif '/' in targets:
                    for ipaddr in ipaddress.IPv4Network(targets).hosts():
                        hosts.append(str(ipaddr))
                # multiple single hosts
                elif ',' in targets:
                    hosts = targets.split(',')
                # single host
                else:
                    hosts.append(targets)
            except Exception as e:
                self.log('hostrange',
                         eargs=repr(e.args[0]),
                         _type='err',
                         end='\n')

        if hosts:
            self.opts['targets']['nmap']['hosts'] = hosts

        return

    def parse_nmap_logfile(self, logfile, privip=False):
        """ parse hosts and ports from nmap xml logfile (-l) """

        nmap = NmapParser()

        try:
            parsed = nmap.parse_fromfile(logfile)
        except:
            self.log('nmap', eargs=f'{logfile}', _type='err', end='\n')
        else:
            for host in parsed.hosts:
                if len(host.hostnames) >= 1:
                    _host = host.hostnames[0]  # better work with hostname
                else:
                    _host = host.address
                    try:
                        if ipaddress.ip_address(_host).is_private:
                            privip = True
                    except:
                        pass  # hostname
                ports = []
                res = host.get_open_ports()
                if res:
                    for port, proto in res:
                        tmp = str(host.get_service(port)).translate(
                            str.maketrans('', '', ':[])')).split()
                        service = self.misc.lookup_port_service(
                            str(port), proto)
                        ports.append([str(port), service])
                    self.opts['targets'][proto].append({
                        'host': _host,
                        'ports': ports,
                        'privip': privip
                    })
                else:
                    # host up but no ports found
                    self.opts['targets']['tcp'].append({
                        'host': _host,
                        'ports': [],
                        'privip': privip
                    })

        return

    def parse_targets(self, args, privip=False):
        """ parse all targets from line specified via URIs (-u) """

        targets = []

        # remove empty ones, if ';' was used at the end without further targets
        if ';' in args:
            targets = [x.strip() for x in args.split(';') if x]
        elif 'person://' in args or 'company://' in args:
            targets.append(args)
        else:
            targets = args.split()

        for t in targets:
            if re.search('^tcp://|^udp://', t):
                ports = []
                part = t.split('://')
                proto = part[0].strip(self.bad_chars)
                self.opts['targets_opts']['target_type'] = proto
                host = part[1].split(':')[0].strip(self.bad_chars)
                try:
                    if ipaddress.ip_address(host).is_private:
                        privip = True
                except:
                    pass  # hostname
                if ':' in part[1]:
                    tmp_ports = part[1].split(':')[1]
                    tmp_ports = tmp_ports.split(',')
                    for p in tmp_ports:
                        if '=' in p:
                            ports.append(p.split('='))
                        else:
                            service = self.misc.lookup_port_service(p)
                            if service:
                                p = f'{p}={service}'
                            else:
                                # assign default service if not given
                                p = f'{p}=default'
                            ports.append(p.split('='))
                self.opts['targets'][proto].append({
                    'host': host,
                    'ports': ports,
                    'privip': privip
                })
            elif re.search('^http://|^https://', t):
                self.opts['targets_opts']['target_type'] = t.split(':')[0]
                self.opts['targets']['web'] = t.split(',')
                # add leading '/' to url if not given
                #for u in t.split(','):
                #  if not u.endswith('/'):
                #    u = f'{u}/'
                #  self.opts['targets']['web'].append(u)
            elif re.search(
                    '\
        ^mail://|^person://|^company://|^domain://|^lan://|^wifi://', t):
                self.opts['targets_opts']['target_type'] = t.split(':')[0]
                line = list(
                    filter(None,
                           [l.replace('://', ':') for l in t.split(';')]))
                line = dict(l.split(':') for l in line)
                for k in line.keys():
                    if k == 'mail' or k == 'person' or k == 'company' or k == 'domain':
                        self.opts['targets']['social'][k] = line[k].split(',')
                    else:
                        self.opts['targets'][k] = line[k].split(',')
            else:
                unknown_mode = t.split('://')[0]
                self.log('mode', eargs=unknown_mode, _type='err', end='\n')

        return

    def parse_cmdline(self):
        """ parse command line """

        try:
            opts, args = getopt.getopt(
                self.opts['cmdline'][1:],
                't:u:l:o:i:I:x:X:T:M:P:k:rR:c:vdCp:m:a:VH')
        except getopt.GetoptError as err:
            self.log('default', eargs=repr(err), _type='err', end='\n')

        for o, a in opts:
            if o == '-t':
                if a == '?':
                    Usage.nmap_mode_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_nmap_targets(a)
            elif o == '-u':
                if a == '?':
                    Usage.host_mode_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_targets(a)
            elif o == '-l':
                self.parse_nmap_logfile(a)
            elif o == '-o':
                if a == '?':
                    Usage.extra_opts_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_extra_opts(a)
            elif o == '-i':
                if a == '?':
                    Usage.modules_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_modules(a, 'in_modules')
            elif o == '-I':
                if a == '?':
                    Usage.tools_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_tools(a, 'in_tools')
            elif o == '-x':
                if a == '?':
                    Usage.modules_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_modules(a, 'ex_modules')
            elif o == '-X':
                if a == '?':
                    Usage.tools_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_tools(a, 'ex_tools')
            elif o == '-T':
                self.opts['t_workers'] = a
            elif o == '-M':
                self.opts['m_workers'] = a
            elif o == '-P':
                self.opts['p_workers'] = a
            elif o == '-k':
                self.opts['timeout'] = a
            elif o == '-r':
                self.opts['report'] = True
            elif o == '-R':
                self.opts['nullscan_dir'] = a + '-' + TODAY
            elif o == '-c':
                self.opts['config']['file'] = a
            elif o == '-v':
                self.opts['verbose'] = True
            elif o == '-d':
                self.opts['debug'] = True
            elif o == '-C':
                self.opts['check_tools'] = True
            elif o == '-p':
                if a == '?':
                    Usage.print_tools_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_print_tools(a)
            elif o == '-m':
                if a == '?':
                    Usage.add_module_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_add_module_tool(a, 'add_module')
            elif o == '-a':
                if a == '?':
                    Usage.add_tool_usage()
                    os._exit(SUCCESS)
                else:
                    self.parse_add_module_tool(a, 'add_tool')
            elif o == '-V':
                self.log(VERSION, _type='msg', end='\n')
                os._exit(SUCCESS)
            elif o == '-H':
                Usage.usage()
                os._exit(SUCCESS)
            else:
                self.log('cmdopt', eargs=o, _type='err', end='\n')

        return
Exemple #5
0
class Misc:
  """ class for miscellaneous stuff """


  def __init__(self):
    """ constructor """

    # logger
    self.logger = Logger()
    self.log = self.logger.log

    # file i/o
    self.file = File()

    # original terminal state
    self.term_fd = sys.stdin.fileno()
    self.term_state = termios.tcgetattr(self.term_fd)

    return


  def reset_terminal(self):
    """ reset terminal to original state """

    termios.tcsetattr(self.term_fd, termios.TCSADRAIN, self.term_state)

    return


  def grep_tools(self, py_file):
    """ Find all tools (method names) of given file """

    tools = []

    lines = self.file.read_file(py_file)
    for line in lines:
      if 'def ' in line:
        method = line.split()[1].split('(')[0]
        if not method.startswith('_'):
          tools.append(method)

    return sorted(list(set(tools)))


  def find_py_files(self, root_path):
    """ Find all py files with some exceptions of given root_path """

    py_files = []

    files = glob.glob(f'{root_path}/*/*.py', recursive=True)
    [py_files.append(x) for x in files if '__ini' not in x and '/libs' not in x]

    return sorted(list(set(py_files)))


  def kill_process(self, pattern, signal='TERM'):
    """ kill (all) processes matched by pattern """

    cmd = ['pkill', f'-{signal}', '-f', pattern]
    subprocess.run(cmd)

    return


  def remove_empty_files_dirs(self, rootpath):
    """ delete empty (log-)files and directories """

    threads = 20
    files = glob.glob(f'{rootpath}/**', recursive=True)

    # files
    with ThreadPoolExecutor(max_workers=threads) as exe:
      # files
      for f in files:
        if os.path.isfile(f):
          fsize = os.path.getsize(f)
          if fsize <= 3:
            exe.submit(os.unlink, f)

      # directories
      for f in files:
        if os.path.isdir(f) and not os.listdir(f):
          exe.submit(os.rmdir, f)
          self.remove_empty_files_dirs(rootpath)

    return


  def lookup_port_service(self, port, proto='tcp'):
    """ return corresponding service from services.csv for a given port """

    service = None
    services = self.file.read_csv_file(f'{ROOT_PATH}/lists/services.csv')

    for s in services:
      if port in s and proto in s:
        service = s[0]

    return service


  def print_tool(self, opts):
    """ print tool and a description of it """

    tools = []

    m = Module(MOD_PATH)
    m.get_docstrings()

    if 'all' in opts.keys():
      for tool in m.docstrings.keys():
        tools.append(tool)
    else:
      for moddir, module in opts.items():
        if module:
          for mod in module:
            for tool in m.docstrings:
              if m.docstrings[tool]['moddir'] == moddir and \
                m.docstrings[tool]['module'] == mod:
                  tools.append(tool)
        else:
          for k, v in m.docstrings.items():
            if moddir in v['moddir']:
              tools.append(k)

    tools = sorted(list(set(tools)))
    for tool in tools:
      self.log(m.docstrings[tool]['moddir'] + '/' +
        m.docstrings[tool]['module'] + '/' + tool + ' - ' +
        m.docstrings[tool]['descr'], end='\n', _type='vmsg')

    return


  def add_mod_tool(self, form, opts):
    """ create a new module and/or add tool to exisiting module """

    template = f'{MOD_PATH}libs/template.py'
    modpart = f"{MOD_PATH}{opts['moddir']}/"
    moddir = modpart
    module = f"{modpart}{opts['modname']}.py"
    tmpmod = f'{modpart}tmpxxx.py'

    if form == 'mod':
      with open(template, 'r') as fin:
        if not os.path.isdir(moddir):
          self.file.make_dir(moddir)
        with open(module, 'w') as fout:
          for line in fin:
            if '<template>.py' in line:
              fout.write(line.replace('<template>', opts['modname']))
            elif '<file>' in line:
              fout.write(line.replace('<file>', opts['modname']))
            elif '<class>' in line:
              fout.write(line.replace('<class>',
                opts['modname'].capitalize()))
            elif '<mod>' in line:
              fout.write(line.replace('<mod>', opts['modname']))
            else:
              fout.write(line)
      self.log(f"Created module {opts['moddir']}/{opts['modname']}.py\n",
        _type='msg')

    # append new tools
    try:
      shutil.copyfile(module, tmpmod)
      with open(tmpmod, 'a') as fout:
        fout.write('\n\n  @tool\n')
        fout.write(f"  def {opts['func']}(self):\n")
        fout.write('    """\n')
        fout.write('    DESCR: <REPLACE MANUALLY>\n')
        fout.write('    TOOLS: <ADD MANUALLY>\n')
        fout.write('    """\n\n')
        fout.write(f"    opts = \'{' '.join(opts['args'])}\'\n\n")
        fout.write(f"    self._run_tool(\'{opts['tool']}\', opts)\n\n")
        fout.write('    return\n')
      shutil.move(tmpmod, module)
      self.log(f"Added tool {opts['func']} to {opts['moddir']}/" +
        f"{opts['modname']}.py\n", _type='msg')
    except:
      self.log('add_tool', _type='err', end='\n')

    return