def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "request_command"): command = layer.request_command if hasattr(layer, "request_parameter"): parameter = layer.request_parameter if command == "AUTH": # TODO : handle more types of auth if parameter == "PLAIN": session["auth_process_plain"] = True elif session["auth_process_plain"]: session["auth_process_plain"] = False current_creds.username, current_creds.password = utils.parse_sasl_creds( command, "PLAIN") if current_creds.username and hasattr(layer, "response_indicator"): indicator = layer.response_indicator if indicator == "+OK": logger.found( session, "credentials found: {} -- {}".format(current_creds.username, current_creds.password)) session.validate_credentials() elif indicator == "-ERR": session.invalidate_credentials_and_clear_session()
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "response_code"): code = int(layer.response_code) if code == 230 and current_creds.username: logger.found( session, "credentials found: {} -- {}".format(current_creds.username, current_creds.password)) session.validate_credentials() elif code == 430: session.invalidate_credentials_and_clear_session() elif hasattr(layer, "request_command"): command = layer.request_command if command == "USER": current_creds.username = layer.request_arg elif command == "PASS": current_creds.password = layer.request_arg
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "request_command"): command = layer.request_command if command == "LOGIN": tokens = layer.request.split('"') current_creds.username = tokens[1] current_creds.password = tokens[3] # Due to an incompatibility with "old" tshark versions, we cannot use response_command :( elif hasattr(layer, "response"): command = layer.response if " LOGIN " in command: status = layer.response_status if status == "OK": logger.found( session, "credentials found: {} -- {}".format( current_creds.username, current_creds.password)) session.validate_credentials() elif status == "NO" or status == "BAD": session.invalidate_credentials_and_clear_session()
def _process_packet(session: Session, packet: Packet, must_inspect_strings: bool): """ Processes a single packet within its context thanks to the `Session` instance. Parameters ---------- session : Session The session the packet belongs to. packet : Packet To packet to be analysed. must_inspect_strings : bool Whether strings in the packet should be inspected or not. Can be pretty heavy on the CPU. """ 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() ntlmssp.analyse(session, layer) # Analyse the layer with the appropriate parser if layer_name in parsers: session.protocol = layer_name.upper() parsers[layer_name].analyse(session, layer) 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 test_malformed(self): from credslayer.core import manager pcap = FileCapture("samples/smb-crash.pcap") self.assertRaises(manager.MalformedPacketException, manager._process_packet, Session(pcap[8]), pcap[8], False) pcap.close()
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "name"): current_creds.username = layer.name if hasattr(layer, "simple"): current_creds.password = layer.simple session["auth_process"] = True if session["auth_process"] and hasattr(layer, "resultcode"): result_code = int(layer.resultcode) session["auth_process"] = False if result_code == 0: logger.found( session, "credentials found: {} -- {}".format(current_creds.username, current_creds.password)) session.validate_credentials()
def analyse(session: Session, layer: Layer): 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") or hasattr(layer, "query"): # Yes this try except is ugly, but there's a bug before tshark 3.0 which prevents us to use response_code # See https://www.wireshark.org/docs/dfref/i/imap.html try: response_code = int(layer.response_code, 16) auth_successful = response_code == 0 except AttributeError: auth_successful = hasattr(layer, "query") if current_creds.username and auth_successful: 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)) session.validate_credentials()
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "req_command"): command = layer.req_command if hasattr(layer, "req_parameter"): parameter = layer.req_parameter if command == "AUTH": # TODO : handle more types of auth if parameter.startswith("LOGIN"): session["auth_process_login"] = True elif parameter.startswith("PLAIN"): # TODO: not tested, find a pcap session["auth_process_plain"] = True if session["auth_process_login"]: if hasattr(layer, "auth_username"): username = layer.auth_username current_creds.username = b64decode(username).decode() elif hasattr(layer, "auth_password"): password = layer.auth_password current_creds.password = b64decode(password).decode() session["auth_process_login"] = False elif session["auth_process_plain"]: if hasattr(layer, "auth_username"): b64_auth = layer.auth_username current_creds.username, current_creds.password = utils.parse_sasl_creds(b64_auth, "PLAIN") session["auth_process_plain"] = False if hasattr(layer, "response_code"): response_code = int(layer.response_code) if response_code == 235: logger.found(session, "credentials found: {} -- {}".format(current_creds.username, current_creds.password)) session.validate_credentials()
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "version"): session.protocol = "SNMPv" + str(int(layer.version) + 1) elif hasattr(layer, "msgversion"): session.protocol = "SNMPv" + layer.msgversion else: session.protocol = "SNMPv?" if hasattr(layer, "community") \ and (session["community_string"] is None or session["community_string"] != layer.community): current_creds.password = session["community_string"] = layer.community logger.found(session, "community string found: " + layer.community) session.validate_credentials() elif hasattr(layer, "msgusername") and layer.msgusername != "msgUserName: "******"username"] is None or session["username"] != layer.msgusername): current_creds.username = session["username"] = layer.msgusername logger.found(session, "username found: " + layer.msgusername) session.validate_credentials()
def analyse(session: Session, layer: Layer): if not hasattr(layer, "data"): return current_creds = session.credentials_being_built # Sometimes tshark returns multiple Data fields data_fields = layer.data.all_fields for data in data_fields: if session["data_being_built"] is None: session["data_being_built"] = "" session["user_being_built"] = session["pass_being_built"] = False try: data = data.binary_value.decode() except UnicodeDecodeError: continue lowered_data = data.lower().strip() for username_ask in POTENTIAL_USERNAME_ASK: if lowered_data.endswith(username_ask): session["user_being_built"] = True break else: # Yes for loops have elses ;) if lowered_data.endswith("password:"******"pass_being_built"] = True elif current_creds.password: for auth_success_msg in POTENTIAL_AUTH_SUCCESS: if auth_success_msg in lowered_data: logger.found( session, "credentials found: {} -- {}".format( current_creds.username, current_creds.password)) session.validate_credentials() return for auth_error_msg in POTENTIAL_AUTH_ERROR: if auth_error_msg in lowered_data: session.invalidate_credentials_and_clear_session() else: session["data_being_built"] += data if "\r" in session["data_being_built"] or "\n" in session[ "data_being_built"]: data_being_built = session["data_being_built"].replace("\r", "")\ .replace("\n", "")\ .replace("\x00", "") if session["user_being_built"]: username = data_being_built if _is_username_duplicated(username): username = "".join([ username[i] for i in range(0, len(username), 2) ]) current_creds.username = username session["user_being_built"] = False elif session["pass_being_built"]: current_creds.password = data_being_built session["pass_being_built"] = False session["data_being_built"] = ""
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if hasattr(layer, "request_uri"): extension = layer.request_uri.split(".")[-1] if extension in HTTP_IGNORED_EXTENSIONS: return # Ignore Certificate Status Protocol if hasattr(layer, "request_full_uri" ) and layer.request_full_uri.startswith("http://ocsp."): return 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) # Don't validate those credentials return # 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) # Don't validate those credentials return 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)) session.validate_credentials()
def analyse(session: Session, layer: Layer): current_creds = session.credentials_being_built if current_creds and hasattr(layer, "nt_status"): status = int(layer.nt_status) if status == 0: # LOGON SUCCESS logger.found( session, "{} found: {}".format(current_creds.context["version"], current_creds.hash)) session.validate_credentials() elif status == 3221225581: # LOGON FAILED session.invalidate_credentials_and_clear_session() if hasattr(layer, "ntlmssp_messagetype"): message_type = int(layer.ntlmssp_messagetype, 16) if message_type == 2: # Challenge session["challenge"] = layer.ntlmssp_ntlmserverchallenge.replace( ":", "") elif message_type == 3: # Auth username = layer.ntlmssp_auth_username domain = layer.ntlmssp_auth_domain challenge = session["challenge"] if len(username) == 1 or len(domain) == 1: username, domain = _fix_tshark_widechar_issue(layer) if not challenge: challenge = "CHALLENGE_NOT_FOUND" if domain == "NULL": domain = "" if hasattr(layer, "ntlmssp_ntlmv2_response"): current_creds.context["version"] = "NETNTLMv2" proof = layer.ntlmssp_ntlmv2_response_ntproofstr auth_ntresponse = layer.ntlmssp_ntlmv2_response[len(proof) + 1:] proof = proof.replace(":", "") auth_ntresponse = auth_ntresponse.replace(":", "") current_creds.hash = "{}::{}:{}:{}:{}".format( username, domain, session["challenge"], proof, auth_ntresponse) elif hasattr(layer, "ntlmssp_ntlmclientchallenge"): current_creds.context["version"] = "NETNTLMv1" auth_ntresponse = layer.ntlmssp_auth_ntresponse.replace( ":", "") client_challenge = layer.ntlmssp_ntlmclientchallenge.replace( ":", "").ljust(48, "0") current_creds.hash = "{}::{}:{}:{}:{}".format( username, domain, client_challenge, auth_ntresponse, challenge) else: # Unsupported NTLM format, investigate ? Found a pcap w/o ntlm client challenge field session.invalidate_credentials_and_clear_session()
def analyse(session: Session, layer: Layer): 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"])) session.validate_credentials() return 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