def test_spf_check_host(self): s = spf.SenderPolicyFramework('1.2.3.4', 'king-phisher.com') check_host_result = s.check_host() self.assertIsNotNone(check_host_result) self.assertEqual(check_host_result, 'fail') self.assertEqual(spf.check_host('1.2.3.4', 'king-phisher.com'), 'fail')
def test_spf_rfc7208_macro_expansion(self): s = spf.SenderPolicyFramework('192.0.2.3', 'email.example.com', '*****@*****.**') expand_macro = lambda m: s.expand_macros( m, '192.0.2.3', 'email.example.com', '*****@*****.**' ) self.assertEqual(expand_macro('%{s}'), '*****@*****.**') self.assertEqual(expand_macro('%{o}'), 'email.example.com') self.assertEqual(expand_macro('%{d}'), 'email.example.com') self.assertEqual(expand_macro('%{d4}'), 'email.example.com') self.assertEqual(expand_macro('%{d3}'), 'email.example.com') self.assertEqual(expand_macro('%{d2}'), 'example.com') self.assertEqual(expand_macro('%{d1}'), 'com') self.assertEqual(expand_macro('%{dr}'), 'com.example.email') self.assertEqual(expand_macro('%{d2r}'), 'example.email') self.assertEqual(expand_macro('%{l}'), 'strong-bad') self.assertEqual(expand_macro('%{l-}'), 'strong.bad') self.assertEqual(expand_macro('%{lr}'), 'strong-bad') self.assertEqual(expand_macro('%{lr-}'), 'bad.strong') self.assertEqual(expand_macro('%{l1r-}'), 'strong') self.assertEqual(expand_macro('%{ir}.%{v}._spf.%{d2}'), '3.2.0.192.in-addr._spf.example.com') self.assertEqual(expand_macro('%{lr-}.lp._spf.%{d2}'), 'bad.strong.lp._spf.example.com') self.assertEqual(expand_macro('%{lr-}.lp.%{ir}.%{v}._spf.%{d2}'), 'bad.strong.lp.3.2.0.192.in-addr._spf.example.com') self.assertEqual(expand_macro('%{ir}.%{v}.%{l1r-}.lp._spf.%{d2}'), '3.2.0.192.in-addr.strong.lp._spf.example.com') self.assertEqual(expand_macro('%{d2}.trusted-domains.example.net'), 'example.com.trusted-domains.example.net')
def signal_button_clicked_verify_spf(self, button): sender_email = self.gobjects['entry_source_email_smtp'].get_text() if not utilities.is_valid_email_address(sender_email): gui_utilities.show_dialog_warning('Warning', self.parent, 'Can not check SPF records for an invalid source email address.\n') return True spf_test_ip = mailer.guess_smtp_server_address(self.config['smtp_server'], (self.config['ssh_server'] if self.config['smtp_ssh_enable'] else None)) if not spf_test_ip: gui_utilities.show_dialog_warning('Warning', self.parent, 'Skipping spf policy check because the smtp server address could not be reliably detected') return True spf_test_sender, spf_test_domain = sender_email.split('@') try: spf_test = spf.SenderPolicyFramework(spf_test_ip, spf_test_domain, spf_test_sender) spf_result = spf_test.check_host() except spf.SPFError as error: gui_utilities.show_dialog_warning('Warning', self.parent, "Done, encountered exception: {0}.\n".format(error.__class__.__name__)) return True if not spf_result: gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'No SPF records found.') else: if spf_result is 'fail': gui_utilities.show_dialog_info('SPF Check Results:', self.parent, 'SPF exists with a hard fail. Your messages will probably be blocked.') elif spf_result is 'softfail': gui_utilities.show_dialog_info('SPF Check Results', self.parent, 'SPF Exists with a soft fail. Your messages have strong possiblity of being blocked. Check your logs.') return True return True
def main(): parser = argparse.ArgumentParser(description='King Phisher SPF Check Utility', conflict_handler='resolve') utilities.argp_add_args(parser) parser.add_argument('smtp_server_ip', help='the ip address of the sending smtp server') parser.add_argument('target_email', help='the email address that messages are from') arguments = parser.parse_args() utilities.configure_stream_logger(arguments.logger, arguments.loglvl) server_ip = arguments.smtp_server_ip target_email = arguments.target_email if not ipaddress.is_valid(server_ip): color.print_error('the smtp server ip address specified is invalid') return if not '@' in target_email: target_email = utilities.random_string_lower_numeric(8) + '@' + target_email color.print_status('target email appears to be just a domain, changed to: ' + target_email) if not utilities.is_valid_email_address(target_email): color.print_error('the email address specified is invalid') return spf_sender, spf_domain = target_email.split('@') spf_test = spf.SenderPolicyFramework(server_ip, spf_domain, spf_sender) try: result = spf_test.check_host() except spf.SPFParseError as error: color.print_error('check_host failed with error: permerror (parsing failed)') color.print_error('error reason: ' + error.message) return except spf.SPFPermError as error: color.print_error('check_host failed with error: permerror') color.print_error('error reason: ' + error.message) return except spf.SPFTempError as error: color.print_error('check_host failed with error: temperror') color.print_error('error reason: ' + error.message) return if not result: color.print_status('no spf policy was found for the specified domain') return color.print_good("spf policy result: {0}".format(result)) color.print_status('top level spf records found:') match = spf_test.match for record_id, record in enumerate(spf_test.records.values(), 1): color.print_status(" #{0} {1: <10} {2}".format( record_id, ('(matched)' if match.record == record else ''), record.domain )) for directive_id, directive in enumerate(record.directives, 1): color.print_status(" #{0}.{1} {2: <10} {3}".format( record_id, directive_id, ('(matched)' if match.record == record and match.directive == directive else ''), directive ))
def test_spf_evaluate_mechanism_temperror(self): s = spf.SenderPolicyFramework('1.2.3.4', 'doesnotexist.king-phisher.com') eval_mech = lambda m, r: s._evaluate_mechanism(s.ip_address, s.domain, s.sender, m, r) self.assertFalse(eval_mech('a', None)) self.assertFalse(eval_mech('exists', None)) self.assertFalse(eval_mech('mx', None))
def test_spf_evaluate_mechanism(self): s = spf.SenderPolicyFramework('1.2.3.4', 'doesnotexist.king-phisher.com') eval_mech = lambda m, r: s._evaluate_mechanism(s.ip, s.domain, s.sender, m, r) self.assertTrue(eval_mech('all', None)) self.assertTrue(eval_mech('exists', '%{d2}')) self.assertTrue(eval_mech('ip4', '1.2.3.0/24')) self.assertTrue(eval_mech('ip4', '1.2.3.4')) self.assertFalse(eval_mech('ip4', '1.1.1.0/24'))
def test_spf_evaluate_mechanism_temperror(self): s = spf.SenderPolicyFramework('1.2.3.4', 'doesnotexist.king-phisher.com') eval_mech = lambda m, r: s._evaluate_mechanism(s.ip, s.domain, s.sender, m, r) with self.assertRaises(spf.SPFTempError): eval_mech('a', None) with self.assertRaises(spf.SPFTempError): eval_mech('exists', None) with self.assertRaises(spf.SPFTempError): eval_mech('mx', None)
def test_spf_evaluate_mechanism_permerror(self): s = spf.SenderPolicyFramework('1.2.3.4', 'doesnotexist.king-phisher.com') eval_mech = lambda m, r: s._evaluate_mechanism(s.ip, s.domain, s.sender, m, r) with self.assertRaises(spf.SPFPermError): eval_mech('ip4', 'thisisnotanetwork') with self.assertRaises(spf.SPFPermError): eval_mech('ip6', 'thisisnotanetwork') with self.assertRaises(spf.SPFPermError): eval_mech('fake', None)
def main(): parser = argparse.ArgumentParser(description='King Phisher SPF Check Utility', conflict_handler='resolve') parser.add_argument('-v', '--version', action='version', version=parser.prog + ' Version: ' + version.version) parser.add_argument('-L', '--log', dest='loglvl', action='store', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='CRITICAL', help='set the logging level') parser.add_argument('smtp_server_ip', help='the ip address of the sending smtp server') parser.add_argument('target_email', help='the email address that messages are from') arguments = parser.parse_args() logging.getLogger('').setLevel(logging.DEBUG) console_log_handler = logging.StreamHandler() console_log_handler.setLevel(getattr(logging, arguments.loglvl)) console_log_handler.setFormatter(logging.Formatter("%(levelname)-8s %(message)s")) logging.getLogger('').addHandler(console_log_handler) server_ip = arguments.smtp_server_ip target_email = arguments.target_email if not utilities.is_valid_ip_address(server_ip): print('[-] the smtp server ip address specified is invalid') return if not utilities.is_valid_email_address(target_email): print('[-] the email address specified is invalid') return spf_sender, spf_domain = target_email.split('@') spf_test = spf.SenderPolicyFramework(server_ip, spf_domain, spf_sender) try: result = spf_test.check_host() except spf.SPFPermError: print('[-] check_host failed with error: permerror') return except spf.SPFTempError: print('[-] check_host failed with error: temperror') return if not result: print('[*] no spf policy was found for the specified domain') return print("[+] spf policy result: {0}".format(result)) print('[*] top level spf records found:') for rid in range(len(spf_test.spf_records)): record = spf.record_unparse(spf_test.spf_records[rid]) print("[*] #{0} {1: <10} {2}".format(rid + 1, ('(matched)' if rid == spf_test.spf_record_id else ''), record))
def _sender_precheck_spf(self): spf_check_level = self.config['spf_check_level'] if not spf_check_level: return True if not utilities.is_valid_email_address(self.config['mailer.source_email_smtp']): self.text_insert('WARNING: Can not check SPF records for an invalid source email address.\n') return True spf_test_ip = mailer.guess_smtp_server_address(self.config['smtp_server'], (self.config['ssh_server'] if self.config['smtp_ssh_enable'] else None)) if not spf_test_ip: self.text_insert('Skipped checking the SPF policy because the SMTP server address could not be detected.\n') self.logger.warning('skipping spf policy check because the smtp server address could not be reliably detected') return True self.logger.debug('detected the smtp server address as ' + str(spf_test_ip)) spf_test_sender, spf_test_domain = self.config['mailer.source_email_smtp'].split('@') self.text_insert("Checking the SPF policy of target domain '{0}'... ".format(spf_test_domain)) try: spf_test = spf.SenderPolicyFramework(spf_test_ip, spf_test_domain, spf_test_sender) spf_result = spf_test.check_host() except spf.SPFError as error: self.text_insert("done, encountered exception: {0}.\n".format(error.__class__.__name__)) return True if not spf_result: self.text_insert('done, no policy was found.\n') else: self.text_insert('done.\n') dialog_title = 'Sender Policy Framework Failure' dialog_message = None if spf_check_level == 1 and spf_result in [SPFResult.FAIL, SPFResult.SOFT_FAIL]: dialog_message = 'The configuration fails the domains SPF policy.\nMessages may be marked as forged.' elif spf_check_level == 2 and not spf_result in [SPFResult.NEUTRAL, SPFResult.PASS]: dialog_message = 'The configuration does not pass the domains SPF policy.' spf_result = spf_result or 'N/A (No policy found)' self.text_insert("{0}SPF policy result: {1}\n".format(('WARNING: ' if spf_result.endswith('fail') else ''), spf_result)) if dialog_message: dialog_message += '\n\nContinue sending messages anyways?' if not gui_utilities.show_dialog_yes_no(dialog_title, self.parent, dialog_message): self.text_insert('Sending aborted due to the SPF policy.\n') return False return True
def test_spf_nonexistent_domain(self): s = spf.SenderPolicyFramework('1.2.3.4', 'doesnotexist.king-phisher.com') self.assertIsNone(s.check_host()) self.assertIsNone( spf.check_host('1.2.3.4', 'doesnotexist.king-phisher.com'))
def signal_button_clicked_sender_start(self, button): required_settings = { 'mailer.webserver_url': 'Web Server URL', 'mailer.company_name': 'Company Name', 'mailer.source_email': 'Source Email', 'mailer.subject': 'Subject', 'mailer.html_file': 'Message HTML File', 'mailer.target_file': 'Target CSV File' } for setting, setting_name in required_settings.items(): if not self.config.get(setting): gui_utilities.show_dialog_warning( "Missing Required Option: '{0}'".format(setting_name), self.parent, 'Return to the Config tab and set all required options') return if not setting.endswith('_file'): continue file_path = self.config[setting] if not (os.path.isfile(file_path) and os.access(file_path, os.R_OK)): gui_utilities.show_dialog_warning( 'Invalid Option Configuration', self.parent, "Setting: '{0}'\nReason: the file could not be read.". format(setting_name)) return if not utilities.is_valid_email_address( self.config['mailer.source_email']): gui_utilities.show_dialog_warning( 'Invalid Option Configuration', self.parent, 'Setting: \'mailer.source_email\'\nReason: the email address is invalid.' ) return if not self.config.get('smtp_server'): gui_utilities.show_dialog_warning( 'Missing SMTP Server Setting', self.parent, 'Please configure the SMTP server') return self.text_insert('Checking the target URL... ') try: test_webserver_url( self.config['mailer.webserver_url'], self.config['server_config']['server.secret_id']) except Exception: self.text_insert('failed') if not gui_utilities.show_dialog_yes_no( 'Unable To Open The Web Server URL', self.parent, 'The URL may be invalid, continue sending messages anyways?' ): self.text_insert(', sending aborted.\n') return self.text_insert(', error ignored.\n') else: self.text_insert('success, done.\n') if self.config['autocheck_spf']: spf_test_ip = mailer.guess_smtp_server_address( self.config['smtp_server'], (self.config['ssh_server'] if self.config['smtp_ssh_enable'] else None)) if not spf_test_ip: self.text_insert( 'Skipped checking the SPF policy because the SMTP server address could not be detected.\n' ) self.logger.warning( 'skipping spf policy check because the smtp server address could not be reliably detected' ) else: self.logger.debug('detected the smtp server address as ' + str(spf_test_ip)) spf_test_sender, spf_test_domain = self.config[ 'mailer.source_email'].split('@') self.text_insert( "Checking the SPF policy of target domain '{0}'... ". format(spf_test_domain)) try: spf_test = spf.SenderPolicyFramework( spf_test_ip, spf_test_domain, spf_test_sender) spf_result = spf_test.check_host() except spf.SPFError as error: spf_result = None self.text_insert( "done, encountered exception: {0}.\n".format( error.__class__.__name__)) else: if spf_result: self.text_insert('done.\n') self.text_insert("{0}SPF policy result: {1}\n".format( ('WARNING: ' if spf_result.endswith('fail') else ''), spf_result)) if spf_result == 'fail' and not gui_utilities.show_dialog_yes_no( 'Sender Policy Framework Failure', self.parent, 'The configuration fails the domains SPF policy.\nContinue sending messages anyways?' ): self.text_insert( 'Sending aborted due to a failed SPF policy.\n' ) return else: self.text_insert('done, no policy was found.\n') # after this the operation needs to call self.sender_start_failure to quit if self.sender_thread: return self.parent.save_config() self.gobjects['button_mail_sender_start'].set_sensitive(False) self.gobjects['button_mail_sender_stop'].set_sensitive(True) self.progressbar.set_fraction(0) self.sender_thread = mailer.MailSenderThread( self.config, self.config['mailer.target_file'], self.parent.rpc, self) # verify settings missing_files = self.sender_thread.missing_files() if missing_files: text = ''.join( map(lambda f: "Missing required file: '{0}'\n".format(f), missing_files)) self.sender_start_failure('Missing required files', text) return # connect to the smtp server if self.config['smtp_ssh_enable']: while True: self.text_insert('Connecting to SSH... ') login_dialog = dialogs.KingPhisherClientSSHLoginDialog( self.config, self.parent) login_dialog.objects_load_from_config() response = login_dialog.interact() if response == Gtk.ResponseType.CANCEL: self.sender_start_failure(text='canceled.\n') return if self.sender_thread.server_ssh_connect(): self.text_insert('done.\n') break self.sender_start_failure( ('Connection Failed', 'Failed to connect to the SSH server.'), 'failed.\n', retry=True) self.text_insert('Connecting to SMTP server... ') if not self.sender_thread.server_smtp_connect(): self.sender_start_failure( ('Connection Failed', 'Failed to connect to the SMTP server.'), 'failed.\n') return self.text_insert('done.\n') parsed_target_url = urllib.parse.urlparse( self.config['mailer.webserver_url']) landing_page_hostname = parsed_target_url.netloc landing_page = parsed_target_url.path landing_page = landing_page.lstrip('/') self.parent.rpc('campaign/landing_page/new', self.config['campaign_id'], landing_page_hostname, landing_page) self.sender_thread.start() self.gobjects['togglebutton_mail_sender_pause'].set_sensitive(True)
def main(): parser = argparse.ArgumentParser( conflict_handler='resolve', description=PARSER_DESCRIPTION, epilog=PARSER_EPILOG, formatter_class=argparse.RawTextHelpFormatter ) utilities.argp_add_args(parser) parser.add_argument('smtp_server_ip', help='the ip address of the sending smtp server') parser.add_argument('target_email', help='the email address that messages are from') parser.add_argument('--dns-timeout', dest='dns_timeout', default=spf.DEFAULT_DNS_TIMEOUT, metavar='TIMEOUT', type=int, help='the timeout for dns queries') arguments = parser.parse_args() server_ip = arguments.smtp_server_ip target_email = arguments.target_email if not ipaddress.is_valid(server_ip): color.print_error('the smtp server ip address specified is invalid') return if not '@' in target_email: target_email = utilities.random_string_lower_numeric(8) + '@' + target_email color.print_status('target email appears to be just a domain, changed to: ' + target_email) if not utilities.is_valid_email_address(target_email): color.print_error('the email address specified is invalid') return spf_sender, spf_domain = target_email.split('@') spf_test = spf.SenderPolicyFramework(server_ip, spf_domain, sender=spf_sender, timeout=arguments.dns_timeout) try: result = spf_test.check_host() except spf.SPFParseError as error: color.print_error('check_host failed with error: permerror (parsing failed)') color.print_error('error reason: ' + error.message) return except spf.SPFPermError as error: color.print_error('check_host failed with error: permerror') color.print_error('error reason: ' + error.message) return except spf.SPFTempError as error: color.print_error('check_host failed with error: temperror') color.print_error('error reason: ' + error.message) return if not result: color.print_status('no spf policy was found for the specified domain') return color.print_good("spf policy result: {0}".format(result)) color.print_status('top level spf records found:') match = spf_test.match for record_id, record in enumerate(spf_test.records.values(), 1): color.print_status(" #{0} {1: <10} {2}".format( record_id, ('(matched)' if match.record == record else ''), record.domain )) for directive_id, directive in enumerate(record.directives, 1): color.print_status(" #{0}.{1: <2} {2: <10} {3}".format( record_id, directive_id, ('(matched)' if match.record == record and match.directive == directive else ''), directive ))