def __init__(self): BaseConsole.__init__(self) self.analyzer = PcapAnalyzer() self.config = {'mobile': False, 'ip-layer' : False} self.current_session = None
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])