From 93b1040fe76d5314c466993d6da056676455ce65 Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Wed, 27 Oct 2021 03:43:42 +0200 Subject: [PATCH] updated decode-spam-headers.py --- phishing/decode-spam-headers.py | 195 +++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 5 deletions(-) diff --git a/phishing/decode-spam-headers.py b/phishing/decode-spam-headers.py index d6d0258..c41ea91 100644 --- a/phishing/decode-spam-headers.py +++ b/phishing/decode-spam-headers.py @@ -81,6 +81,13 @@ # Usage: # ./decode-spam-headers [options] # +# NOTICE: +# Parts of this code contain fragments copied from the following places: +# +# 1) testEmailIntelligence(): +# source: https://github.com/nquinlan/Email-Intelligence +# authored by: Nick Quinlan (nick@nicholasquinlan.com) +# # Requirements: # - packaging # - dnspython @@ -318,14 +325,20 @@ class SMTPHeadersAnalysis: 'trusteer', 'trustlook', 'virusblokada', 'virustotal', 'virustotalcloud', 'webroot', 'yandex', 'yandexbot', 'zillya', 'zonealarm', 'zscaler', '-sea-', 'perlmx', 'trustwave', 'mailmarshal', 'tmase', 'startscan', 'fe-etp', 'jemd', 'suspicious', 'grey', 'infected', 'unscannable', - 'dlp-', 'sanitize', 'mailscan', 'barracuda', + 'dlp-', 'sanitize', 'mailscan', 'barracuda', 'clearswift', 'messagelabs', 'msw-jemd', 'fe-etp', 'symc-ess', + 'starscan', 'mailcontrol', ) Interesting_Headers = ( 'mailgun', 'sendgrid', 'mailchimp', 'x-ses', 'x-avas', 'X-Gmail-Labels', 'X-vrfbldomain', 'mandrill', 'bulk', 'sendinblue', 'amazonses', 'mailjet', 'postmark', 'postfix', 'dovecot', 'roundcube', 'seg', '-IP', 'crosspremises', 'brightmail', 'check', 'exim', 'postfix', 'exchange', 'microsoft', 'office365', - 'dovecot', 'sendmail', 'score', 'report', 'status', + 'dovecot', 'sendmail', 'score', 'report', 'status', 'benchmarkemail', 'bronto', 'X-Complaints-To', + 'X-Roving-ID', 'X-DynectEmail-Msg', 'X-elqPod', 'X-EMV-MemberId', 'e2ma', 'fishbowl', 'eloop', 'X-Google-Appengine-App-Id', + 'X-ICPINFO', 'x-locaweb-id', 'X-MC-User', 'mailersend', 'MailiGen', 'Mandrill', 'MarketoID', 'X-Messagebus-Info', + 'Mixmax', 'X-PM-Message-Id', 'postmark', 'X-rext', 'responsys', 'X-SFDC-User', 'salesforce', 'x-sg-', 'x-sendgrid-', + 'silverpop', '.mkt', 'X-SMTPCOM-Tracking-Number', 'X-vrfbldomain', 'verticalresponse', + 'yesmail', ) Headers_Known_For_Breaking_Line = ( @@ -1107,7 +1120,7 @@ Results will be unsound. Make sure you have pasted your headers with correct spa i += j - 1 - if headers[-1][1].lower() == 'content-type': + if len(headers) > 0 and len(headers[-1]) > 1 and headers[-1][1].lower() == 'content-type': m = re.search(r'boundary="([^"]+)"', headers[-1][2], re.I) if m: boundary = m.group(1) @@ -1205,6 +1218,7 @@ Results will be unsound. Make sure you have pasted your headers with correct spa ('Other unrecognized Spam Related Headers', self.testSpamRelatedHeaders), ('Other interesting headers', self.testInterestingHeaders), ('Security Appliances Spotted', self.testSecurityAppliances), + ('Email Providers Infrastructure Clues', self.testEmailIntelligence), ) for i in range(len(SMTPHeadersAnalysis.Handled_Spam_Headers)): @@ -1355,6 +1369,171 @@ Results will be unsound. Make sure you have pasted your headers with correct spa lines.append(line) return '\n'.join(lines) + def testEmailIntelligence(self): + service = [] + value = self.text + + # + # NOTICE: + # This code below was copied from the following repository: + # https://github.com/nquinlan/Email-Intelligence + # + # and is authored solely by Nick Quinlan (nick@nicholasquinlan.com). + # + + # Amazon SES + if re.search(r'^X-SES-Outgoing:', value, re.I|re.S) or "Amazon SES" in value or "Amazon SES".lower() in value.lower() or "AmazonSES".lower() in value.lower(): + service.append(("Amazon SES", "http://aws.amazon.com/ses/")) + + # BenchmarkEmail.com + if re.search(r'www.benchmarkemail.com', value, re.I|re.S) or "BenchmarkEmail".lower() in value.lower(): + service.append(("BenchmarkEmail", "http://benchmarkemail.com/")) + + # Bronto + if re.search(r'd=bronto.com;', value, re.I|re.S) or "Bronto".lower() in value.lower(): + service.append(("Bronto", "http://bronto.com/")) + + # Campaign Monitor + if re.search(r'^X-Complaints-To: abuse@cmail\d{1,2}\.com', value, re.I|re.S) or "Campaign Monitor".lower() in value.lower() or "CampaignMonitor".lower() in value.lower(): + service.append(("Campaign Monitor", "https://www.campaignmonitor.com")) + + # Constant Contact + if re.search(r'^X-Roving-ID:', value, re.I|re.S) or "Constant Contact".lower() in value.lower() or "ConstantContact".lower() in value.lower(): + service.append(("Constant Contact", "https://www.constantcontact.com")) + + # Dyn + if re.search(r'^X-DynectEmail-Msg-(Key|Hash):', value, re.I|re.S) or "Dyn".lower() in value.lower(): + service.append(("Dyn", "https://dyn.com/")) + + # Eloqua + if re.search(r'^X-elqPod:', value, re.I|re.S) or "Eloqua".lower() in value.lower(): + service.append(("Eloqua", "http://www.eloqua.com/")) + + # Email Vision + if re.search(r'^X-EMV-MemberId:', value, re.I|re.S) or "Emailvision".lower() in value.lower(): + service.append(("Emailvision", "https://www.emailvision.com/")) + + # Emma + if re.search(r'd=e2ma\.net;', value, re.I|re.S) or "Emma".lower() in value.lower(): + service.append(("Emma", "https://myemma.com/")) + + # ExactTarget + if re.search(r'^x-job: \d{3,}_\d{3,}$', value, re.I|re.S) and re.search(r'mta[\d]*\.[\w-\.]+\.[a-z]{2,}', value, re.I|re.S) or "ExactTarget".lower() in value.lower(): + service.append(("ExactTarget", "http://www.exacttarget.com/")) + + # Fishbowl + if re.search(r'^X-Mailer: Fishbowl', value, re.I|re.S) or "Fishbowl".lower() in value.lower(): + service.append(("Fishbowl", "https://www.fishbowl.com/")) + + # Gold Lasso + if re.search(r'^X-Mailer: Eloop Mailer', value, re.I|re.S) or "Gold Lasso".lower() in value.lower() or "GoldLasso".lower() in value.lower(): + service.append(("Gold Lasso", "https://www.goldlasso.com/")) + + # Google App Engine + if re.search(r'^X-Google-Appengine-App-Id:', value, re.I|re.S) or "Google App Engine".lower() in value.lower() or "GoogleApp".lower() in value.lower() or "AppEngine".lower() in value.lower(): + service.append(("Google App Engine", "https://developers.google.com/appengine/docs/python/mail/sendingmail")) + + # iContact + if re.search(r'^X-ICPINFO:', value, re.I|re.S) or "iContact".lower() in value.lower(): + service.append(("iContact", "https://www.icontact.com/")) + + # Listrak + if re.search(r'^Received: from [\w-]+\.listrak\.com', value, re.I|re.S) or "Listrak".lower() in value.lower(): + service.append(("Listrak", "https://www.listrak.com/")) + + # Locaweb + if re.search(r'^x-locaweb-id:', value, re.I|re.S) or "Locaweb".lower() in value.lower(): + service.append(("Locaweb", "https://www.locaweb.com.br/")) + + # Mailchimp + if re.search(r'^X-MC-User:', value, re.I|re.S) or "MailChimp".lower() in value.lower(): + service.append(("MailChimp", "https://mailchimp.com/")) + + # MailerLite + if re.search(r'd=ml.mailersend.com;', value, re.I|re.S) or "MailerLite".lower() in value.lower(): + service.append(("MailerLite", "https://www.mailerlite.com/")) + + # Mailgun + if re.search(r'^X-Mailgun-Sid:', value, re.I|re.S) or re.search(r'X-Mailgun-Variables:', value, re.I|re.S) or "Mailgun".lower() in value.lower(): + service.append(("Mailgun", "https://www.mailgun.com/")) + + # Mailigen + if re.search(r'^X-Mailer: MailiGen', value, re.I|re.S) or "Mailigen".lower() in value.lower(): + service.append(("Mailigen", "http://www.mailigen.com/")) + + # Mailjet + if re.search(r's=mailjet;', value, re.I|re.S) or "Mailjet".lower() in value.lower(): + service.append(("Mailjet", "https://www.mailjet.com/")) + + # Mandrill + if re.search(r'^X-Mandrill-User:', value, re.I|re.S) or "Mandrill".lower() in value.lower(): + service.append(("Mandrill", "https://mandrillapp.com/")) + + # Marketo + if re.search(r'^X-MarketoID:', value, re.I|re.S) or "Marketo".lower() in value.lower(): + service.append(("Marketo", "https://www.marketo.com/")) + + # Message Bus + if re.search(r'^X-Messagebus-Info:', value, re.I|re.S) or "Message Bus".lower() in value.lower() or "MessageBus".lower() in value.lower(): + service.append(("Message Bus", "https://messagebus.com/")) + + # Mixmax + if re.search(r'^X-Mailer: Mixmax', value, re.I|re.S) or "Mixmax".lower() in value.lower(): + service.append(("Mixmax", "https://mixmax.com/")) + + # Postmark + if re.search(r'^X-PM-Message-Id:', value, re.I|re.S) or "Postmark".lower() in value.lower(): + service.append(("Postmark", "https://postmarkapp.com/")) + + # Responsys + if re.search(r'^X-rext:', value, re.I|re.S) or "Responsys".lower() in value.lower(): + service.append(("Responsys", "https://www.responsys.com/")) + + # Sailthru + if re.search(r'^X-Mailer: sailthru.com$', value, re.I|re.S) or "Sailthru".lower() in value.lower(): + service.append(("Sailthru", "https://www.sailthru.com/")) + + # Salesforce + if re.search(r'^X-SFDC-User:', value, re.I|re.S) or "Salesforce".lower() in value.lower(): + service.append(("Salesforce", "https://www.salesforce.com/")) + + # SendGrid + if re.search(r'^X-(SG|SENDGRID)-EID:', value, re.I|re.S) or "SendGrid".lower() in value.lower(): + service.append(("SendGrid", "https://sendgrid.com/")) + + # Silverpop + if re.search(r'^Received: from [\w\.]+\.mkt\d{3,}\.com', value, re.I|re.S): # Not proprietary, but likely only Silverpo or "Silverpop".lower() in value.lower()p + service.append(("Silverpop", "https://www.silverpop.com/")) + + # SMTP.com + if re.search(r'^X-SMTPCOM-Tracking-Number:', value, re.I|re.S) or "SMTP.com".lower() in value.lower(): + service.append(("SMTP.com", "https://smtp.com/")) + + # VerticalResponse + if re.search(r'^X-vrfbldomain:', value, re.I|re.S) and re.search(r'^X-vrpod:', value, re.I|re.S) and re.search(r'^X-vrrpmm:', value, re.I|re.S) or "VerticalResponse".lower() in value.lower(): + service.append(("VerticalResponse", "http://www.verticalresponse.com/")) + + # Yesmail + if re.search(r's=yesmail.?;', value, re.I|re.S) or re.search(r'^Received: from [\w\.\-]+postdirect.com', value, re.I|re.S) or "Yesmail".lower() in value.lower(): + service.append(("Yesmail", "https://www.yesmail.com/")) + + if len(service) == 0: + return [] + + result = f'- Mail contents analysis shown that this e-mail passed through the following third-party Mail providers:\n\n' + + for svc in service: + svcname = self.logger.colored(svc[0], 'green') + result += f'\t- {svcname} - url: {svc[1]}\n' + + return { + 'header': '', + 'value' : '', + 'analysis' : result, + 'description' : '', + } + + def testSecurityAppliances(self): result = '' vals = [x.lower() for x in SMTPHeadersAnalysis.Header_Keywords_That_May_Contain_Spam_Info] @@ -2102,7 +2281,7 @@ Results will be unsound. Make sure you have pasted your headers with correct spa if num == -1: return [] result = f'- Sophos Email Appliance Spam report:\n' - self.securityAppliances.add('Sophos Email Appliance') + self.securityAppliances.add('Sophos Email Appliance (PureMessage)') report = {} value = SMTPHeadersAnalysis.flattenLine(value) @@ -2365,6 +2544,7 @@ Src: https://www.cisco.com/c/en/us/td/docs/security/esa/esa11-1/user_guide/b_ESA elif k == 'E': v0 = self.logger.colored(v, 'red') result += f'\t\t- {v0}\n' + self.securityAppliances.add(f'{v} AV') elif k == 'e': vs = v.split("'") @@ -2870,8 +3050,10 @@ Src: https://www.cisco.com/c/en/us/td/docs/security/esa/esa11-1/user_guide/b_ESA if firstHopDomain1.lower() != senderDomain.lower(): response = None try: + if domain.endswith('.'): domain = domain[:-1] response = dns.resolver.resolve(domain, 'TXT') - except dns.resolver.NoAnswer as e: + + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e: response = [] spf = False @@ -3301,6 +3483,9 @@ Src: https://www.cisco.com/c/en/us/td/docs/security/esa/esa11-1/user_guide/b_ESA for i in range(len(path)): elem = path[i] + if len(elem) < 2: + continue + num += 1 s = '-->' if i > 0: