CheckEmailAuthenticity
Checks the authenticity of an email based on the email's SPF, DMARC, and DKIM.
python · Phishing
Details
| ID | CheckEmailAuthenticity |
|---|---|
| Language | python |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10404775 |
| Tags | phishing ews email |
README
Checks the authenticity of an email based on the email’s SPF, DMARC, and DKIM.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | phishing, ews, email |
| Cortex XSOAR Version | 5.0.0 |
Used In
This script is used in the following playbooks and scripts.
- Agari Message Remediation - Agari Phishing Defense
- Email Headers Check - Generic
- Phishing - Generic v3
- Phishing Investigation - Generic v2
- Report Categorization - Cofense Triage v3
Inputs
| Argument Name | Description |
|---|---|
| headers | A list of dictionaries of headers in the form of “Header name”:”Header value”. |
| original_authentication_header | The header that holds the original Authentication-Results header value. This can be used when an intermediate server changes the original email and holds the original header value in a different header. Note - Use this only if you trust the server creating this header. |
| SPF_override_none | Override value for SPF=None. |
| SPF_override_neutral | Override value for SPF=neutral. |
| SPF_override_pass | Override value for SPF=pass. |
| SPF_override_fail | Override value for SPF=fail. |
| SPF_override_softfail | Override value for SPF=softfail. |
| SPF_override_temperror | Override value for SPF=temperror. |
| SPF_override_permerror | Override value for SPF=permerror. |
| DKIM_override_none | Override value for DKIM=none. |
| DKIM_override_pass | Override value for DKIM=pass. |
| DKIM_override_fail | Override value for DKIM=fail. |
| DKIM_override_policy | Override value for DKIM=policy. |
| DKIM_override_neutral | Override value for DKIM=neutral. |
| DKIM_override_temperror | Override value for DKIM=temperror. |
| DKIM_override_permerror | Override value for DKIM=permerror. |
| DMARC_override_none | Override value for DMARC=none. |
| DMARC_override_pass | Override value for DMARC=pass. |
| DMARC_override_fail | Override value for DMARC=fail. |
| DMARC_override_temperror | Override value for DMARC=temperror. |
| DMARC_override_permerror | Override value for DMARC=permerror. |
Outputs
| Path | Description | Type |
|---|---|---|
| Email.SPF.MessageID | SPF ID | String |
| Email.SPF.Validation-Result | Validation Result. Possible values are “None”, “Neutral”, “Pass”, “Fail”, “SoftFail”, “TempError”, and “PermError”. | String |
| Email.SPF.Reason | Reason for the SPF result, which is located in the headers of the email. | String |
| Email.SPF.Sender-IP | Email sender IP address. | String |
| Email.DKIM.Message-ID | DKIM ID. | String |
| Email.DKIM.Reason | DKIM reason (if found). | String |
| Email.DMARC.Message-ID | DMARC ID. | String |
| Email.DMARC.Validation-Result | DMARC reason. Possible values are “None”, “Pass”, “Fail”, “Temperror”, and “Permerror”. | String |
| Email.DMARC.Tags | DMARC Tags (if found) | String |
| Email.DMARC.From-Domain | Sender’s Domain | String |
| Email.DKIM.Signing-Domain | Sender’s Domain | String |
| Email.AuthenticityCheck | Possible values are be: Fail / Suspicious / Undetermined / Pass | Unknown |
| Email.DKIM | DKIM information extracted from the email. | Unknown |
| Email.SPF | SPF information extracted from the email. | Unknown |
| Email.DMARC | DMARC information extracted from the email. | Unknown |
| Email.DKIM.Validation-Result | Validation result. Possible values are “None”, “Pass”, “Fail”, “Policy”, “Neutral”, “Temperror”, and “Permerror”. | Unknown |
import demistomock as demisto from CheckEmailAuthenticity import get_authentication_value, get_spf, main MOCK_HEADERS = [ {"name": "Message-ID", "value": "test_message_id"}, { "name": "received-spf", "value": "Pass (test.com: domain of test.com designates 8.8.8.8 as permitted sender)" "receiver=test.com; client-ip=8.8.8.8; helo=test.com;", }, { "name": "Authentication-Results", "value": "spf=pass (sender IP is 8.8.8.8) smtp.mailfrom=test.com; dkim=fail (body hash did not verify) " "header.d=test.com; dmarc=pass action=none header.from=test.com;compauth=pass reason=100", }, ] MOCK_HEADERS_DIFFERENT_AUTH_HEADER = [ {"name": "Message-ID", "value": "test_message_id"}, { "name": "received-spf", "value": "Pass (test.com: domain of test.com designates 8.8.8.8 as permitted sender)" "receiver=test.com; client-ip=8.8.8.8; helo=test.com;", }, {"name": "Authentication-Results", "value": "mock_different_value"}, ] EMAIL_KEY = ( "Email(val.Headers.filter(function(header) { return header && header.name === 'Message-ID' && " "header.value === 'test_message_id';}))" ) def test_check_email_auth(mocker): mocker.patch.object(demisto, "args", return_value={"headers": MOCK_HEADERS}) mocker.patch.object(demisto, "results") main() results = demisto.results.call_args[0] # assert (str(results[0]['EntryContext'])) == '3' dmarc = results[0]["EntryContext"][f"{EMAIL_KEY}.DMARC"] assert dmarc["Validation-Result"] == "pass" assert dmarc["Signing-Domain"] == "test.com" spf = results[0]["EntryContext"][f"{EMAIL_KEY}.SPF"] assert spf["Validation-Result"] == "pass" assert spf["Sender-IP"] == "8.8.8.8" dkim = results[0]["EntryContext"][f"{EMAIL_KEY}.DKIM"] assert dkim["Validation-Result"] == "fail" assert dkim["Reason"] == "body hash did not verify" # AuthenticityCheck fails because DKIM failed assert results[0]["EntryContext"][f"{EMAIL_KEY}.AuthenticityCheck"] == "Fail" def test_get_authentication_value(): """ Given: an authenticator header that is not a part of the given headers array. When: there is an intermediate server which changes the email and holds the original value of the header in a different header. Then: override the given authenticator headers in the headers array and use the original one. """ original_authentication_header_included_in_headers = "Authentication-Results" original_authentication_header_not_included_in_headers = "Authentication-Results-Not-Included" assert ( get_authentication_value(MOCK_HEADERS_DIFFERENT_AUTH_HEADER, original_authentication_header_not_included_in_headers) == "mock_different_value" ) assert ( get_authentication_value(MOCK_HEADERS, original_authentication_header_included_in_headers) == "spf=pass (sender IP is 8.8.8.8) smtp.mailfrom=test.com; dkim=fail (body hash did not verify) " "header.d=test.com; dmarc=pass action=none header.from=test.com;compauth=pass reason=100" ) def test_get_spf_formats(): spf_with_parentheses = "Pass (test.com: domain of test.com designates 8.8.8.8 as permitted sender)" spf_without_parentheses = "Pass test.com: domain of test.com designates 8.8.8.8 as permitted sender" spf_data = get_spf(auth=None, spf=spf_with_parentheses) assert spf_data["Validation-Result"] == "pass" assert spf_data["Sender-IP"] == "8.8.8.8" spf_data = get_spf(auth=None, spf=spf_without_parentheses) assert spf_data["Validation-Result"] == "pass" assert spf_data["Sender-IP"] == "8.8.8.8"