Beispiel #1
0
 def __init__(self):
     BaseConsole.__init__(self)
     self.analyzer = PcapAnalyzer()
     self.config = {'mobile': False, 'ip-layer' : False}
     self.current_session = None
Beispiel #2
0
class ClippConsole(BaseConsole):

    intro = """
      ________    ___         ___    ________    ________   
     |\   ____\  |\  \       |\  \  |\   __  \  |\   __  \  
     \ \  \___|  \ \  \      \ \  \ \ \  \|\  \ \ \  \|\  \ 
      \ \  \      \ \  \      \ \  \ \ \   ____\ \ \   ____\ 
       \ \  \___ _ \ \  \____  \ \  \ \ \  \___|  \ \  \___|
        \ \_______\ \ \_______\ \ \__\ \ \__\      \ \__\   
         \|_______|  \|_______|  \|__|  \|__|       \|__|       

    Command Line Interface Packet Parser.

    Type "help" or "?" to list available commands.

    Type "help <command>" to see the command's help.
    """

    prompt = Colors.BOLD + Colors.F_BLUE + "clipp>>" + Colors.RESET_ALL

    def __init__(self):
        BaseConsole.__init__(self)
        self.analyzer = PcapAnalyzer()
        self.config = {'mobile': False, 'ip-layer' : False}
        self.current_session = None

    def do_clean(self, args):
        self.analyzer.sessions.clear()
        self.print_info("All sessions have been erased.")
        self.current_session = None
        self.reset_prompt()

    def reset_prompt(self):
        self.prompt = Colors.BOLD + Colors.F_BLUE + "clipp>>" + Colors.RESET_ALL

    @docopt_cmd
    def do_load(self, args):
        """Usage: load <filename>

        Loads pcap file and parse it.
        """
        try:
            self.analyzer.set_filepath(args['<filename>'])
            self.analyzer.parse_file(self.config['mobile'], self.config.get('ip-layer', False))
        except Exception as e:
            print(e)

    @docopt_cmd
    def do_sessions(self, args):
        """Usage: sessions [[<session_id>] | [(filter <colname> <pattern>)]]

        Print session list, or enter session if <session_id> is given.

        Options:

        filter <colname> <pattern>    only display sessions matching <pattern> in column <colname>
        """
        headers = OrderedDict([('session_id', 'SESSION_ID'), 
            ('ip_src', 'IP_SRC'),
            ('domainsrc', 'DOMAIN_SRC'),
            ('sport', 'PORT_SRC'),
            ('ip_dst', 'IP_DST'),
            ('domaindst', 'DOMAIN_DST'),
            ('dport', 'PORT_DST'),
            ('proto', 'PROTO'),
            ('pkts', 'PACKETS'),
            ('tot_len', 'LENGTH')])
        if args['<session_id>'] in self.analyzer.sessions.keys():
            self.prompt = Colors.BOLD + Colors.F_BLUE + "clipp[%s]>>" % (args['<session_id>']) + Colors.RESET_ALL
            self.current_session = args['<session_id>']
        elif args['filter']:
            colname = args['<colname>']
            pattern = str(args['<pattern>'])
            matches = [key for key, value in headers.iteritems() if colname.lower() == value.lower()]
            if len(matches) == 1 and pattern:
                self.print_session_table(headers, matches.pop(), pattern)
            else:
                self.print_warning("Column {} does not exist.".format(colname))
        else:
            self.print_session_table(headers)

    def print_session_table(self, headers, colname=None, pattern=None):
        data = []
        for key, session in self.analyzer.sessions.iteritems():
            session_dict = session.serialize()
            if colname:
                if pattern in str(session_dict[colname]):
                    data.append([key] + [session_dict[hkey] for hkey in headers.keys()[1:]])
            else:
                data.append([key] + [session_dict[hkey] for hkey in headers.keys()[1:]])
        if len(data) > 0:
            print(to_text([headers.values()] + data))

    def do_info(self, args):
        if self.current_session:
            session = self.analyzer.sessions[self.current_session]
            self.print_default("Session infos :")
            print("Key : \t\t %s" % self.current_session)
            print("NB Packets : \t %d" % session.nb_pkts)
            print("Total length : \t %d" % session.tot_len)
            print("Protocol : \t %s" % session.proto)
            print("IP src : \t %s" % session.ip_src)
            print("IP dst : \t %s" % session.ip_dst)
            print("Port src : \t %d" % session.sport)
            print("Port dst : \t %d" % session.dport)

    def do_show(self, args):
        if self.current_session:
            session = self.analyzer.sessions[self.current_session]
            data = []
            header = ['PKT NUM', 'IP SRC', 'PORT SRC', 'IP DST', 'PORT DST', 'LENGTH']
            for index, pkt in enumerate(session.packets):
                data.append([index,
                             self.analyzer.ip_to_str(pkt.raw_ip.src),
                             pkt.raw_ip.data.sport,
                             self.analyzer.ip_to_str(pkt.raw_ip.dst),
                             pkt.raw_ip.data.dport,
                             len(pkt.data)])
            if len(data) > 0:
                print(to_text([header] + data))

    def do_back(self, args):
        self.current_session = None
        self.reset_prompt()

    @docopt_cmd
    def do_stream(self, args):
        """Usage: 
        stream [-f=<fmt> | --format=<fmt>] [-p=<pkt> | --packet=<pkt>]

        Print session data (TCP/UDP).

        -f <fmt>, --format <fmt>    output format, default is "hex": str, urldecode, hex, hexarray, json, base64 (decode)
        -p <pkt>, --packet <pkt>    only print the packet data instead of session data. Default is -1 (all packets).
        """
        if self.current_session:
            pkt = args['--packet'] if args['--packet'] else -1
            fmt = args['--format'] if args['--format'] else 'hex'
            pkt = int(pkt)
            formatter = SessionFormatter(self.analyzer.sessions[self.current_session])
            results = formatter.format(fmt, pkt)
            self.print_stream(results, pkt) if pkt >= 0 else map(lambda tpl : self.print_stream(tpl[1], tpl[0]), enumerate(results))

    def print_stream(self, stream_data, pkt):
        if len(stream_data) > 0:
            self.print_default("%s[Packet %d]%s" % (Colors.F_CYAN, pkt, Colors.RESET_ALL))
            print(stream_data)


    @docopt_cmd
    def do_dump(self, args):
        """Usage: 
        dump [-p=<pkt> |--packet=<pkt>] <filename>

        Dump session (all packets by default) or packet data to file.

        -p=<pkt>, --packet=<pkt>    packet to dump.
        <filename>                  full path to the output file.
        """
        if self.current_session:
            pkt         = int(args['--packet']) if args['--packet'] else -1
            filename    = args['<filename>']
            session     = self.analyzer.sessions[self.current_session]
            with open(filename, 'wb') as output:
                output.write(session.packets[pkt].data) if pkt >= 0 else map(lambda p : output.write(p.data), session.packets)
        else:
            self.print_error("You must select a session first. Use 'sessions <session_id>' to select a session.")

    @docopt_cmd
    def do_search(self, args):
        """Usage: search [options] (-s|-x) <pattern>...

        -s, --string            the string pattern to find.
        -x, --hexstr            the hex string to find.

        Options:
        -c, --case-sensitive    match case sensitive pattern.
        -S, --sessions-only     only print sessions id where the pattern was found.
        """
        searcher = SearchUtils(self.analyzer.sessions)
        fmt     = 'str' if args['--string'] else 'hex'
        pattern = ' '.join(args['<pattern>']) if fmt == 'str' else ''.join(args['<pattern>'])
        results = searcher.search(pattern, fmt, self.current_session, args['--case-sensitive'])
        self.print_search_results(results, args['--sessions-only'], pattern)

    def print_search_results(self, results, sessions_only, pattern):
        if results:
            if sessions_only:
                [self.print_default(key) for key in results.keys()]
            else:
                for skey, listtuple in results.iteritems():
                    self.print_default("%sSession %s%s" % (Colors.BOLD + Colors.F_MAGENTA, skey, Colors.RESET_ALL))
                    for pindex, indexes, fmt in listtuple:
                        pkt = self.analyzer.sessions[skey].packets[pindex]
                        res_data = self.format_search_results(pattern, pkt, indexes, fmt)
                        self.print_default("{}[Packet {}]:".format(Colors.F_CYAN, pindex, Colors.RESET_ALL))
                        for res in res_data:
                            output = "{}".format(res)
                            self.print_default(output)

    def format_search_results(self, pattern, pkt, indexes, fmt):
        return [self.format_search_result(pattern, pkt, index, fmt) for index in indexes]

    def format_search_result(self, pattern, pkt, index, fmt):
        threshold = 40
        if fmt == 'hex':
            data = pkt.data.encode('hex')
        elif fmt == 'str':
            data = pkt.strings
        original = data[index:index+len(pattern)]
        start = index - threshold if index > threshold else 0
        end = index + threshold if len(data) - index > threshold else len(data)
        prefix = '[...]' if start > 0 else ''
        suffix = '[...]' if end < len(data) else ''
        return prefix + data[start:index] + Colors.BOLD + Colors.F_RED + original + Colors.RESET_ALL + data[index+len(pattern):end] + suffix
    
    @docopt_cmd
    def do_http(self, args):
        """Usage: http [options]

        Options:
        --headers   only print http headers.
        --body      only print http body.
        """
        if self.current_session:
            session = self.analyzer.sessions[self.current_session]
            for index, pkt in enumerate(session.packets):
                if pkt.http:
                    self.print_default("{}[Packet %d]{}".format(Colors.F_CYAN, index, Colors.RESET_ALL))
                    if args['--headers']:
                        self.print_default(str(pkt.http.headers))
                    elif args['--body']:
                        self.print_default(str(pkt.http.body))
                    else:
                        self.print_default(str(pkt.http.headers))
                        self.print_default(str(pkt.http.body))
    
    @docopt_cmd
    def do_extract(self, args):
        """Usage: extract [options] (-p=<pkt>|--packet=<pkt>) 

        -p=<pkt>, --packet=<pkt>    packet number (mandatory).
        -u, --unquote               urldecode the data.
        """
        if self.current_session:
            formatter = SessionFormatter(self.analyzer.sessions[self.current_session])
            pkt = int(args['--packet'])
            if args['--unquote']:
                kv_data = formatter.unquote_pkt(pkt).split('&')
            else:
                kv_data = formatter.strings_pkt(pkt).split('&')
            for kv in kv_data:
                try:
                    params = kv.split('=')
                    self.print_default('Key   : %s' % params[0])
                    json_data = format_json(params[1])
                    self.print_default('Value : ')
                    print json_data
                except Exception:
                    self.print_default('Value : %s' % params[1])
            

    def complete_load(self, text, args, begidx, endidx):
        return self.autocomplete_path(text, args, begidx, endidx)

    def complete_sessions(self, text, args, begidx, endidx):
        return self.autocomplete_session_key(text, args, begidx, endidx)

    def complete_stream(self, text, args, begidx, endidx):
        params = ['-f', '-p', '--format', '--packet', 'str', 'json', 'urldecode', 'base64', 'hex', 'hexarray']
        return filter(lambda s: s.startswith(text), params)

    def autocomplete_session_key(self, text, args, begidx, endidx):
        sub_cmd = 'filter'
        columns = ['IP_SRC', 'IP_DST', 'PORT_SRC', 'PORT_DST', 'DOMAIN_SRC', 'DOMAIN_DST', 'PROTO', 'LENGTH', 'PACKETS']
        args = args.split()
        if sub_cmd in args:
            return filter(lambda s : s.lower().startswith(text.lower()), columns)
        else:
            return filter(lambda s: s.startswith(text), self.analyzer.sessions.keys() + [sub_cmd])