def process_sessions_remaining_content(self) -> List[Credentials]: from csl.core import logger remaining = [session for session in self if not session.credentials_being_built.is_empty()] if len(remaining) > 0: logger.info("Interesting things have been found but the tool weren't able validate them: ") # List things that haven't been reported (sometimes the success indicator has # not been captured and credentials stay in the session without being logged) for session in remaining: logger.info(session, str(session.credentials_being_built)) return remaining
def _process_packet(packet: Packet, must_inspect_strings): # We only support tcp & udp packets for now if "tcp" not in packet and "udp" not in packet: return session = _sessions.get_session_of(packet) if len(packet.layers ) > 3: # == tshark parsed something else than ETH, IP, TCP for layer in packet.layers[3:]: layer_name = layer.layer_name if hasattr(layer, "_ws_malformed_expert"): raise MalformedPacketException( "[{}] session contains malformed packet in layer '{}'". format(session, layer_name)) # Not based on layer name, can be found in different layers if hasattr(layer, "nt_status") or ( hasattr(layer, "ntlmssp_identifier") and layer.ntlmssp_identifier == "NTLMSSP"): session.protocol = layer_name.upper() are_credentials_valid = ntlmssp.analyse(session, layer) if are_credentials_valid: session.validate_credentials() # Analyse the layer with the appropriate parser if layer_name in parsers: session.protocol = layer_name.upper() are_credentials_valid = parsers[layer_name].analyse( session, layer) if are_credentials_valid: session.validate_credentials() if must_inspect_strings: strings = utils.extract_strings_splitted_on_end_of_line_from(packet) emails_found = extract.extract_emails(strings) credit_cards_found = extract.extract_credit_cards(strings) for email in emails_found: logger.info(session, "Found email address: " + email) for credit_card in credit_cards_found: logger.info( session, "Credit card '{}' found: '{}'".format(credit_card.name, credit_card.number))
def active_processing(interface: str, must_inspect_strings=False, tshark_filter=None, debug=False, decode_as=None, pcap_output=None): global _sessions logger.DEBUG_MODE = debug _sessions = SessionList() _sessions.manage_outdated_sessions() signal.signal(signal.SIGINT, signal_handler) live = pyshark.LiveCapture(interface=interface, bpf_filter=tshark_filter, decode_as=decode_as, output_file=pcap_output) logger.info("Listening on {}...".format(interface)) if debug: live.set_debug() try: for packet in live.sniff_continuously(): try: _process_packet(packet, must_inspect_strings) except MalformedPacketException as e: logger.error(str(e) + ", extractor will keep going") except Exception: traceback.print_exc() logger.error( "An exception occurred but extractor will keep going.") except TSharkCrashException: logger.error("tshark crashed :( Please report the following error :") traceback.print_exc() signal_handler(None, None)
def process_pcap(filename: str, must_inspect_strings=False, tshark_filter=None, debug=False, decode_as=None) -> SessionList: global _sessions logger.DEBUG_MODE = debug _sessions = SessionList() pcap = pyshark.FileCapture(filename, display_filter=tshark_filter, decode_as=decode_as) logger.info("Processing packets in '{}'".format(filename)) if debug: pcap.set_debug() start_time = time.time() for packet in pcap: try: _process_packet(packet, must_inspect_strings) except MalformedPacketException as e: logger.error(str(e) + ", extractor will keep going") except Exception: traceback.print_exc() logger.error( "An exception occurred but extractor will keep going.") _sessions.process_sessions_remaining_content() logger.info("Processed in {0:.3f} seconds.".format(time.time() - start_time)) pcap.close() return _sessions
def analyse(session: Session, layer: Layer) -> bool: current_creds = session.credentials_being_built if hasattr(layer, "server_greeting"): if hasattr(layer, "version"): logger.info(session, "MySQL version: " + layer.version) if hasattr(layer, "salt"): current_creds.context["salt"] = layer.salt if hasattr(layer, "salt2"): current_creds.context["salt2"] = layer.salt2 if hasattr(layer, "client_auth_plugin"): logger.info(session, "MySQL auth plugin: " + layer.client_auth_plugin) if hasattr(layer, "user"): current_creds.username = layer.user current_creds.hash = "".join(layer.passwd.split(":")) if hasattr(layer, "response_code"): response_code = int(layer.response_code, 16) if current_creds.username and response_code == 0: for item in current_creds.context: logger.found( session, "{} found: {}".format(item, current_creds.context[item])) logger.found( session, "credentials found: {} -- {}".format(current_creds.username, current_creds.hash)) return True return False
def analyse(session: Session, layer: Layer) -> bool: current_creds = session.credentials_being_built if hasattr(layer, "authtype"): # values signification can be found here https://www.postgresql.org/docs/8.2/protocol-message-formats.html auth_type = int(layer.authtype) if auth_type == 5: current_creds.context["auth_type"] = "md5" elif auth_type == 4: current_creds.context["auth_type"] = "crypt" elif auth_type == 3: current_creds.context["auth_type"] = "cleartext" elif auth_type == 10: current_creds.context["auth_type"] = "sasl" elif auth_type == 0 and current_creds.username: if current_creds.hash: logger.found( session, "credentials found ! Username: {} | Hash: {} | Salt: {}". format(current_creds.username, current_creds.hash, current_creds.context["salt"])) elif current_creds.password: logger.found( session, "credentials found ! Username: {} | Password: {}".format( current_creds.username, current_creds.password)) else: logger.found( session, "it seems that '{}' authenticated without password".format( current_creds.username)) if "database" in current_creds.context: logger.info("Targeting database '{}'".format( current_creds.context["database"])) return True if hasattr(layer, "parameter_name"): # Sometimes tshark returns multiple fields with the same name parameter_names = layer.parameter_name.all_fields parameter_values = layer.parameter_value.all_fields for i in range(len(parameter_names)): parameter_name = parameter_names[i].show parameter_value = parameter_values[i].show if parameter_name == "user": current_creds.username = parameter_value elif parameter_name == "database": current_creds.context["database"] = parameter_value elif parameter_name == "server_version": logger.info(session, "PostgreSQL version: " + parameter_value) if hasattr(layer, "salt"): current_creds.context["salt"] = layer.salt.replace(":", "") elif hasattr(layer, "password"): if "auth_type" in current_creds.context and current_creds.context[ "auth_type"] != "cleartext": current_creds.hash = layer.password auth_type = current_creds.context["auth_type"] # Remove the hash type from the hash string if current_creds.hash.startswith(auth_type): current_creds.hash = current_creds.hash[len(auth_type):] else: current_creds.password = layer.password return False
def analyse(session: Session, layer: Layer) -> bool: current_creds = session.credentials_being_built if hasattr(layer, "request_uri"): extension = layer.request_uri.split(".")[-1] if extension in HTTP_IGNORED_EXTENSIONS: return False if hasattr(layer, "request_full_uri") and layer.request_full_uri.startswith( "http://ocsp."): # Ignore Certificate Status Protocol return False if hasattr(layer, "authorization"): tokens = layer.authorization.split(" ") if len(tokens) == 2 and tokens[0] == "Basic": try: credentials = base64.b64decode(tokens[1]).decode() colon_index = credentials.find(":") current_creds.username = credentials[:colon_index] current_creds.password = credentials[colon_index + 1:] session[ "authorization_header_uri"] = layer.request_full_uri except UnicodeDecodeError: logger.error("HTTP Basic auth failed: " + tokens) elif len(tokens) == 2 and tokens[0] == "NTLM": pass # Already handled by the NTLMSSP module else: logger.info( session, "Authorization header found: '{}'".format( layer.authorization)) # POST parameters if hasattr(layer, "file_data"): post_content = layer.file_data if len(post_content) <= HTTP_AUTH_MAX_LOGIN_POST_LENGTH: logger.info(session, "POST data found: '{}'".format(post_content)) post_parameters = parse_qs(post_content) # We don't want to interfere with the Authorization header potentially being built credentials = Credentials() credentials.context["Method"] = "POST" credentials.context["URL"] = layer.request_full_uri logger.info(session, "context: " + str(credentials.context)) for parameter in post_parameters: if parameter in HTTP_AUTH_POTENTIAL_USERNAMES: credentials.username = post_parameters[parameter][0] elif parameter in HTTP_AUTH_POTENTIAL_PASSWORDS: credentials.password = post_parameters[parameter][0] if credentials.username: logger.found( session, "credentials found: {} -- {}".format( credentials.username, credentials.password)) session.credentials_list.append(credentials) # We return false to prevent the manager from validating the credentials being built return False # GET parameters elif hasattr(layer, "request_uri_query"): get_parameters = parse_qs(layer.request_uri_query) # We don't want to interfere with the Authorization header potentially being built credentials = Credentials() credentials.context["Method"] = "GET" credentials.context["URL"] = layer.request_full_uri for parameter in get_parameters: if parameter in HTTP_AUTH_POTENTIAL_USERNAMES: credentials.username = get_parameters[parameter][0] elif parameter in HTTP_AUTH_POTENTIAL_PASSWORDS: credentials.password = get_parameters[parameter][0] if credentials.username: logger.found( session, "credentials found: {} -- {}".format( credentials.username, credentials.password)) logger.info(session, "context: " + str(credentials.context)) session.credentials_list.append(credentials) # We return false to prevent the manager from validating the credentials being built return False elif hasattr(layer, "response_for_uri"): if session["authorization_header_uri"] == layer.response_for_uri: # If auth failed + prevent duplicates if layer.response_code == "401" or current_creds in session.credentials_list: session.invalidate_credentials_and_clear_session() else: logger.found( session, "basic auth credentials found: {} -- {}".format( current_creds.username, current_creds.password)) return True return False
if args.listen: ip_filter = "host " + args.filter decode_map = None if args.map: decode_map = {} for map in args.map: tokens = map.split(":") if len(tokens) != 2: parser.error("Invalid port mapping") decode_map["tcp.port==" + tokens[0]] = tokens[1] logger.info("extractor will decode traffic on '{}' as '{}'".format(*tokens)) if args.output: if os.path.isfile(args.output): parser.error(args.output + " already exists") logger.OUTPUT_FILE = open(args.output, "w") if args.listen: if os.geteuid() != 0: print("You must be root to listen on an interface.") exit(1) if args.listen_output and os.path.isfile(args.listen_output):