enhanced decode-spam-headers.py and added exception handling.

This commit is contained in:
Mariusz B. / mgeeky 2021-10-18 17:32:50 +02:00
parent c954e554e6
commit a4230c5ce8
1 changed files with 86 additions and 90 deletions

View File

@ -993,46 +993,72 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
self.headers = self.collect(text) self.headers = self.collect(text)
self.results['Received - Mail Servers Flow'] = self.testReceived() tests = (
self.results['Extracted IP addresses'] = self.testExtractIP() ('Received - Mail Servers Flow', self.testReceived),
self.results['Extracted Domains'] = self.testResolveIntoIP() ('Extracted IP addresses', self.testExtractIP),
self.results['Bad Keywords In Headers'] = self.testBadKeywords() ('Extracted Domains', self.testResolveIntoIP),
self.results['From Address Analysis'] = self.testFrom() ('Bad Keywords In Headers', self.testBadKeywords),
self.results['Subject and Thread Topic Difference'] = self.testSubjecThreadTopic() ('From Address Analysis', self.testFrom),
self.results['Authentication-Results'] = self.testAuthenticationResults() ('Subject and Thread Topic Difference', self.testSubjecThreadTopic),
self.results['ARC-Authentication-Results'] = self.testARCAuthenticationResults() ('Authentication-Results', self.testAuthenticationResults),
self.results['Received-SPF'] = self.testReceivedSPF() ('ARC-Authentication-Results', self.testARCAuthenticationResults),
self.results['Mail Client Version'] = self.testXMailer() ('Received-SPF', self.testReceivedSPF),
self.results['X-Originating-IP'] = self.testXOriginatingIP() ('Mail Client Version', self.testXMailer),
self.results['User-Agent Version'] = self.testUserAgent() ('X-Originating-IP', self.testXOriginatingIP),
self.results['X-Forefront-Antispam-Report'] = self.testForefrontAntiSpamReport() ('User-Agent Version', self.testUserAgent),
self.results['X-Microsoft-Antispam-Mailbox-Delivery'] = self.testAntispamMailboxDelivery() ('X-Forefront-Antispam-Report', self.testForefrontAntiSpamReport),
self.results['X-Microsoft-Antispam Bulk Mail'] = self.testMicrosoftAntiSpam() ('X-Microsoft-Antispam-Mailbox-Delivery', self.testAntispamMailboxDelivery),
self.results['X-Exchange-Antispam-Report-CFA-Test'] = self.testAntispamReportCFA() ('X-Microsoft-Antispam Bulk Mail', self.testMicrosoftAntiSpam),
self.results['Domain Impersonation'] = self.testDomainImpersonation() ('X-Exchange-Antispam-Report-CFA-Test', self.testAntispamReportCFA),
self.results['SpamAssassin Spam Status'] = self.testSpamAssassinSpamStatus() ('Domain Impersonation', self.testDomainImpersonation),
self.results['SpamAssassin Spam Level'] = self.testSpamAssassinSpamLevel() ('SpamAssassin Spam Status', self.testSpamAssassinSpamStatus),
self.results['SpamAssassin Spam Flag'] = self.testSpamAssassinSpamFlag() ('SpamAssassin Spam Level', self.testSpamAssassinSpamLevel),
self.results['SpamAssassin Spam Report'] = self.testSpamAssassinSpamReport() ('SpamAssassin Spam Flag', self.testSpamAssassinSpamFlag),
self.results['OVH\'s X-VR-SPAMCAUSE'] = self.testSpamCause() ('SpamAssassin Spam Report', self.testSpamAssassinSpamReport),
self.results['OVH\'s X-Ovh-Spam-Reason'] = self.testOvhSpamReason() ('OVH\'s X-VR-SPAMCAUSE', self.testSpamCause),
self.results['OVH\'s X-Ovh-Spam-Score'] = self.testOvhSpamScore() ('OVH\'s X-Ovh-Spam-Reason', self.testOvhSpamReason),
self.results['X-Virus-Scan'] = self.testXVirusScan() ('OVH\'s X-Ovh-Spam-Score', self.testOvhSpamScore),
self.results['X-Spam-Checker-Version'] = self.testXSpamCheckerVersion() ('X-Virus-Scan', self.testXVirusScan),
self.results['X-IronPort-AV'] = self.testXIronPortAV() ('X-Spam-Checker-Version', self.testXSpamCheckerVersion),
self.results['X-Mimecast-Spam-Score'] = self.testXMimecastSpamScore() ('X-IronPort-AV', self.testXIronPortAV),
self.results['Spam Diagnostics Metadata'] = self.testSpamDiagnosticMetadata() ('X-Mimecast-Spam-Score', self.testXMimecastSpamScore),
self.results['MS Defender ATP Message Properties'] = self.testATPMessageProperties() ('Spam Diagnostics Metadata', self.testSpamDiagnosticMetadata),
self.results['Message Feedback Loop'] = self.testMSFBL() ('MS Defender ATP Message Properties', self.testATPMessageProperties),
self.results['End-to-End Latency - Message Delivery Time'] = self.testTransportEndToEndLatency() ('Message Feedback Loop', self.testMSFBL),
self.results['X-MS-Oob-TLC-OOBClassifiers'] = self.testTLCOObClasifiers() ('End-to-End Latency - Message Delivery Time', self.testTransportEndToEndLatency),
('X-MS-Oob-TLC-OOBClassifiers', self.testTLCOObClasifiers),
('Other unrecognized Spam Related Headers', self.testSpamRelatedHeaders),
('Other interesting headers', self.testInterestingHeaders),
)
testsDecodeAll = (
('X-Microsoft-Antispam-Message-Info', self.testMicrosoftAntiSpamMessageInfo),
('Decoded Mail-encoded header values', self.testDecodeEncodedHeaders),
)
for testName, testFunc in tests:
try:
logger.dbg(f'Running "{testName}"...')
self.results[testName] = testFunc()
except Exception as e:
logger.err(f'Test: "{testName}" failed: {e} . Use --debug to show entire stack trace.')
if options['debug']:
raise
if self.decode_all: if self.decode_all:
self.results['X-Microsoft-Antispam-Message-Info'] = self.testMicrosoftAntiSpamMessageInfo() for testName, testFunc in tests:
self.results['Decoded Mail-encoded header values'] = self.testDecodeEncodedHeaders() try:
logger.dbg(f'Running "{testName}"...')
self.results[testName] = testFunc()
self.results['Other unrecognized Spam Related Headers'] = self.testSpamRelatedHeaders() except Exception as e:
self.results['Other interesting headers'] = self.testInterestingHeaders() logger.err(f'Test: "{testName}" failed: {e} . Use --debug to show entire stack trace.')
if options['debug']:
raise
return {k: v for k, v in self.results.items() if v} return {k: v for k, v in self.results.items() if v}
@ -1133,9 +1159,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
else: else:
result += f'{self.logger.colored(value, "red")}\n' result += f'{self.logger.colored(value, "red")}\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1171,9 +1194,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result += f'\t- Subject: {self.logger.colored(v1, "green")}\n' result += f'\t- Subject: {self.logger.colored(v1, "green")}\n'
result += f'\t- Thread-Topic: {self.logger.colored(v2, "magenta")}\n' result += f'\t- Thread-Topic: {self.logger.colored(v2, "magenta")}\n'
if len(result) == 0:
return []
return { return {
'header' : f'{header1}, {header2}', 'header' : f'{header1}, {header2}',
'value': f'\n{header1}:\n\t{value1}\n\n {header2}:\n\t{value2}', 'value': f'\n{header1}:\n\t{value1}\n\n {header2}:\n\t{value2}',
@ -1191,9 +1211,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
else: else:
result += f' {value}\n' result += f' {value}\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1290,9 +1307,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
pos += 1 pos += 1
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1305,9 +1319,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result = f'- SpamAssassin version.' result = f'- SpamAssassin version.'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1322,9 +1333,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
value = SMTPHeadersAnalysis.flattenLine(value).replace(' ', '').replace('\t', '') value = SMTPHeadersAnalysis.flattenLine(value).replace(' ', '').replace('\t', '')
result += f'Score: {self.logger.colored(value.strip(), "red")}\n' result += f'Score: {self.logger.colored(value.strip(), "red")}\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1345,9 +1353,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result += tmp + '\n' result += tmp + '\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1555,9 +1560,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result += '\n' result += '\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1643,9 +1645,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result += f'\t- Mail\'s domain should resolve to: \t{self.logger.colored(senderDomain, "green")}\n' result += f'\t- Mail\'s domain should resolve to: \t{self.logger.colored(senderDomain, "green")}\n'
result += f'\t- But instead first hop resolved to:\t{self.logger.colored(firstHopDomain1, "red")}\n' result += f'\t- But instead first hop resolved to:\t{self.logger.colored(firstHopDomain1, "red")}\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -1660,11 +1659,13 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
if value.strip().lower() == 'yes': if value.strip().lower() == 'yes':
result = self.logger.colored(f'- SpamAssassin marked this message as SPAM:\n', 'red') result = self.logger.colored(f'- SpamAssassin marked this message as SPAM:\n', 'red')
result += f'\t- ' + self.logger.colored(value, 'red') + '\n' result += f'\t- ' + self.logger.colored(value, 'red') + '\n'
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
'analysis' : result 'analysis' : result
} }
return [] return []
def testSpamAssassinSpamLevel(self): def testSpamAssassinSpamLevel(self):
@ -1676,15 +1677,14 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
_num = self.logger.colored(str(len(value.strip())), 'red') _num = self.logger.colored(str(len(value.strip())), 'red')
result += f'\t- ' + self.logger.colored(value.strip(), 'red') + f' (number: {_num})\n' result += f'\t- ' + self.logger.colored(value.strip(), 'red') + f' (number: {_num})\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
'analysis' : result 'analysis' : result
} }
return []
def testSpamAssassinSpamReport(self): def testSpamAssassinSpamReport(self):
(num, header, value) = self.getHeader('X-Spam-Report') (num, header, value) = self.getHeader('X-Spam-Report')
if num == -1: return [] if num == -1: return []
@ -1700,15 +1700,14 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result += tmp + '\n' result += tmp + '\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
'analysis' : result 'analysis' : result
} }
return []
def testATPMessageProperties(self): def testATPMessageProperties(self):
(num, header, value) = self.getHeader('X-MS-Exchange-AtpMessageProperties') (num, header, value) = self.getHeader('X-MS-Exchange-AtpMessageProperties')
if num == -1: return [] if num == -1: return []
@ -1720,9 +1719,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
if prop in SMTPHeadersAnalysis.ATP_Message_Properties.keys(): if prop in SMTPHeadersAnalysis.ATP_Message_Properties.keys():
result += f'- ' + self.logger.colored(SMTPHeadersAnalysis.ATP_Message_Properties[prop], 'magenta') + '\n' result += f'- ' + self.logger.colored(SMTPHeadersAnalysis.ATP_Message_Properties[prop], 'magenta') + '\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -2063,9 +2059,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
if addscl: if addscl:
result += tmpfoo result += tmpfoo
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -2157,9 +2150,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
tmp += SMTPHeadersAnalysis.hexdump(base64.b64decode(value)) tmp += SMTPHeadersAnalysis.hexdump(base64.b64decode(value))
tmp += '\n\n\n' tmp += '\n\n\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': '...', 'value': '...',
@ -2234,9 +2224,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
result += tmp + '\n' result += tmp + '\n'
if len(result) == 0:
return []
return { return {
'header' : header, 'header' : header,
'value': value, 'value': value,
@ -2617,5 +2604,14 @@ def main(argv):
else: else:
print(output) print(output)
if not options['nocolor']:
print('''
------------------------------------------
Experiencing a bad-looking output with unprintable characters?
Use -N flag to disable console colors, or switch your console for better UI experience.
''')
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv) main(sys.argv)