예제 #1
0
	def _sender_precheck_source(self):
		valid = utilities.is_valid_email_address(self.config['mailer.source_email'])
		valid = valid and utilities.is_valid_email_address(self.config['mailer.source_email_smtp'])
		if valid:
			return True
		self.text_insert('WARNING: One or more source email addresses specified are invalid.\n')
		if not gui_utilities.show_dialog_yes_no('Invalid Email Address', self.parent, 'One or more source email addresses specified are invalid.\nContinue sending messages anyways?'):
			self.text_insert('Sending aborted due to invalid source email address.\n')
			return False
		return True
예제 #2
0
파일: mail.py 프로젝트: l1k3m/king-phisher
	def _sender_precheck_source(self):
		valid = utilities.is_valid_email_address(self.config['mailer.source_email'])
		valid = valid and utilities.is_valid_email_address(self.config['mailer.source_email_smtp'])
		if valid:
			return True
		self.text_insert('WARNING: One or more source email addresses specified are invalid.\n')
		if not gui_utilities.show_dialog_yes_no('Invalid Email Address', self.parent, 'One or more source email addresses specified are invalid.\nContinue sending messages anyways?'):
			self.text_insert('Sending aborted due to invalid source email address.\n')
			return False
		return True
예제 #3
0
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
			))
예제 #4
0
 def iterate_targets(self, counting=False):
     target_type = self.config['mailer.target_type']
     if target_type == 'single':
         target_name = self.config['mailer.target_name'].split(' ')
         while len(target_name) < 2:
             target_name.append('')
         target = MessageTarget(
             first_name=target_name[0].strip(),
             last_name=target_name[1].strip(),
             email_address=self.config['mailer.target_email_address'].strip(
             ))
         yield target
     elif target_type == 'file':
         for target in _iterate_targets_file(self.target_file):
             missing_fields = target.missing_fields
             if missing_fields:
                 if counting:
                     msg = "Target CSV line {0} skipped due to missing field{1}".format(
                         target.line,
                         ('' if len(missing_fields) == 1 else 's'))
                     msg += ':' + ', '.join(
                         field.replace('_', ' ')
                         for field in missing_fields)
                     self.tab_notify_status(msg)
                 continue
             if not utilities.is_valid_email_address(target.email_address):
                 self.logger.warning(
                     "skipping line {0} in target csv file due to invalid email address: {1}"
                     .format(target.line, target.email_address))
                 continue
             yield target
     else:
         self.logger.error(
             "the configured target type '{0}' is unsupported".format(
                 target_type))
	def mask_string(self, word):
		if utilities.is_valid_email_address(word):
			email_user, email_domain = word.split('@')
			safe_string = "{0}@{1}{2}{3}".format(email_user, email_domain[:1], ('*' * (len(email_domain) - 2)), email_domain[-1:])
		else:
			safe_string = "{0}{1}{2}".format(word[:1], ('*' * (len(word) - 2)), word[-1:])
		return safe_string
예제 #6
0
	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
예제 #7
0
	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
예제 #8
0
 def save_alert_settings(self):
     email_address = gui_utilities.gobject_get_value(
         self.gobjects['entry_email_address'])
     phone_number = gui_utilities.gobject_get_value(
         self.gobjects['entry_sms_phone_number'])
     sms_carrier = gui_utilities.gobject_get_value(
         self.gobjects['combobox_sms_carrier'])
     server_user = self.application.server_user
     if email_address and not utilities.is_valid_email_address(
             email_address):
         gui_utilities.show_dialog_warning(
             'Invalid Email Address', self.parent,
             'The email address you have entered is not valid.')
         return
     if phone_number:
         phone_number = ''.join(d for d in phone_number
                                if d in string.digits)
         if len(phone_number) > 11:
             gui_utilities.show_dialog_warning(
                 'Invalid Phone Number', self.parent,
                 'The phone number must not contain more than 11 digits')
             return
     email_address = utilities.nonempty_string(email_address)
     phone_number = utilities.nonempty_string(phone_number)
     sms_carrier = utilities.nonempty_string(sms_carrier)
     self.application.rpc(
         'db/table/set', 'users', server_user.id,
         ('email_address', 'phone_number', 'phone_carrier'),
         (email_address, phone_number, sms_carrier))
예제 #9
0
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')
	parser.add_argument('--dns-timeout', dest='dns_timeout', default=spf.DEFAULT_DNS_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
			))
예제 #10
0
	def iterate_targets(self, counting=False):
		target_type = self.config['mailer.target_type']
		mailer_tab = self.application.main_tabs['mailer']
		if target_type == 'single':
			target_name = self.config['mailer.target_name'].split(' ')
			while len(target_name) < 2:
				target_name.append('')
			target = MessageTarget(
				first_name=target_name[0].strip(),
				last_name=target_name[1].strip(),
				email_address=self.config['mailer.target_email_address'],
				department=None,
				uid=make_uid()
			)
			mailer_tab.emit('send-target', target)
			yield target
		elif target_type == 'file':
			target_file_h = open(self.target_file, 'rU')
			csv_reader = csv.DictReader(target_file_h, ('first_name', 'last_name', 'email_address', 'department'))
			for line_no, raw_target in enumerate(csv_reader, 1):
				if its.py_v2:
					# this intentionally will cause a UnicodeDecodeError to be raised as is the behaviour in python 3.x
					# when csv.DictReader is initialized
					raw_target = dict((k, (v if v is None else v.decode('utf-8'))) for k, v in raw_target.items())
				for required_field in ('first_name', 'last_name', 'email_address'):
					if raw_target[required_field] is None:
						raw_target = None
						if counting:
							self.tab_notify_status("Target CSV line {0} skipped due to missing field: {1}".format(line_no, required_field.replace('_', ' ')))
						break
				if raw_target is None:
					continue
				department = raw_target['department']
				if department is not None:
					department = department.strip()
					if department == '':
						department = None
				email_address = raw_target['email_address'] or ''
				email_address = email_address.strip()
				target = MessageTarget(
					first_name=raw_target['first_name'].strip(),
					last_name=raw_target['last_name'].strip(),
					email_address=email_address,
					department=department,
					uid=make_uid(),
					line=line_no
				)
				mailer_tab.emit('send-target', target)
				if not target.email_address:
					self.logger.warning("skipping line {0} in target csv file due to missing email address".format(line_no))
					continue
				if not utilities.is_valid_email_address(target.email_address):
					self.logger.warning("skipping line {0} in target csv file due to invalid email address: {1}".format(line_no, email_address))
					continue
				yield target
			target_file_h.close()
		else:
			self.logger.error("the configured target type '{0}' is unsupported".format(target_type))
예제 #11
0
def count_targets_file(target_file):
    count = 0
    for target in _iterate_targets_file(target_file):
        if target.missing_fields:
            continue
        if not utilities.is_valid_email_address(target.email_address):
            continue
        count += 1
    return count
예제 #12
0
 def mask_string(self, word):
     if utilities.is_valid_email_address(word):
         email_user, email_domain = word.split('@')
         safe_string = "{0}@{1}{2}{3}".format(
             email_user, email_domain[:1], ('*' * (len(email_domain) - 2)),
             email_domain[-1:])
     else:
         safe_string = "{0}{1}{2}".format(word[:1], ('*' * (len(word) - 2)),
                                          word[-1:])
     return safe_string
예제 #13
0
	def iterate_targets(self, counting=False):
		target_type = self.config['mailer.target_type']
		if target_type == 'single':
			target_name = self.config['mailer.target_name'].split(' ')
			while len(target_name) < 2:
				target_name.append('')
			target = MessageTarget(
				first_name=target_name[0].strip(),
				last_name=target_name[1].strip(),
				email_address=self.config['mailer.target_email_address'].strip(),
				department=None,
				uid=utilities.make_message_uid()
			)
			yield target
		elif target_type == 'file':
			target_file_h = open(self.target_file, 'rU')
			csv_reader = csv.DictReader(target_file_h, ('first_name', 'last_name', 'email_address', 'department'))
			for line_no, raw_target in enumerate(csv_reader, 1):
				if its.py_v2:
					# this will intentionally cause a UnicodeDecodeError to be raised as is the behaviour in python 3.x
					# when csv.DictReader is initialized
					raw_target = dict((k, (v if v is None else v.decode('utf-8'))) for k, v in raw_target.items())
				for required_field in ('first_name', 'last_name', 'email_address'):
					if raw_target[required_field] is None:
						raw_target = None
						if counting:
							self.tab_notify_status("Target CSV line {0} skipped due to missing field: {1}".format(line_no, required_field.replace('_', ' ')))
						break
				if raw_target is None:
					continue
				department = raw_target['department']
				if department is not None:
					department = department.strip()
					if department == '':
						department = None
				email_address = raw_target['email_address'] or ''
				email_address = email_address.strip()
				target = MessageTarget(
					first_name=raw_target['first_name'].strip(),
					last_name=raw_target['last_name'].strip(),
					email_address=email_address,
					department=department,
					uid=utilities.make_message_uid(),
					line=line_no
				)
				if not target.email_address:
					self.logger.warning("skipping line {0} in target csv file due to missing email address".format(line_no))
					continue
				if not utilities.is_valid_email_address(target.email_address):
					self.logger.warning("skipping line {0} in target csv file due to invalid email address: {1}".format(line_no, email_address))
					continue
				yield target
			target_file_h.close()
		else:
			self.logger.error("the configured target type '{0}' is unsupported".format(target_type))
예제 #14
0
	def test_is_valid_email_address(self):
		valid_emails = [
			'*****@*****.**',
			'*****@*****.**',
			'*****@*****.**',
		]
		invalid_emails = [
			'aliddle.wonderland.com'
			'aliddle+',
			'aliddle@',
			'aliddle',
			'',
			'@wonderland.com',
			'@[email protected]',
			'[email protected]'
		]
		for address in valid_emails:
			self.assertTrue(utilities.is_valid_email_address(address))
		for address in invalid_emails:
			self.assertFalse(utilities.is_valid_email_address(address))
예제 #15
0
    def iterate_targets(self, counting=False):
        """
		Iterate over each of the targets as defined within the configuration.
		If *counting* is ``False``, messages will not be displayed to the end
		user through the notification tab.

		:param bool counting: Whether or not to iterate strictly for counting purposes.
		:return: Each message target.
		:rtype: :py:class:`~.MessageTarget`
		"""
        mailer_tab = self.application.main_tabs['mailer']
        target_type = self.config['mailer.target_type']
        if target_type == 'single':
            target_name = self.config['mailer.target_name'].split(' ')
            while len(target_name) < 2:
                target_name.append('')
            uid_charset = self.config['mailer.message_uid.charset']
            target = MessageTarget(
                first_name=target_name[0].strip(),
                last_name=target_name[1].strip(),
                email_address=self.config['mailer.target_email_address'].strip(
                ),
                uid=utilities.make_message_uid(upper=uid_charset['upper'],
                                               lower=uid_charset['lower'],
                                               digits=uid_charset['digits']))
            if not counting:
                mailer_tab.emit('target-create', target)
            yield target
        elif target_type == 'file':
            for target in _iterate_targets_file(self.target_file,
                                                config=self.config):
                missing_fields = target.missing_fields
                if missing_fields:
                    if counting:
                        msg = "Target CSV line {0} skipped due to missing field{1}".format(
                            target.line,
                            ('' if len(missing_fields) == 1 else 's'))
                        msg += ':' + ', '.join(
                            field.replace('_', ' ')
                            for field in missing_fields)
                        self.tab_notify_status(msg)
                    continue
                if not utilities.is_valid_email_address(target.email_address):
                    self.logger.warning(
                        "skipping line {0} in target csv file due to invalid email address: {1}"
                        .format(target.line, target.email_address))
                    continue
                if not counting:
                    mailer_tab.emit('target-create', target)
                yield target
        else:
            self.logger.error(
                "the configured target type '{0}' is unsupported".format(
                    target_type))
예제 #16
0
 def iterate_targets(self):
     target_type = self.config['mailer.target_type']
     mailer_tab = self.application.main_tabs['mailer']
     if target_type == 'single':
         target_name = self.config['mailer.target_name'].split(' ')
         while len(target_name) < 2:
             target_name.append('')
         target = MessageTarget(
             first_name=target_name[0].strip(),
             last_name=target_name[1].strip(),
             email_address=self.config['mailer.target_email_address'],
             department=None,
             uid=make_uid())
         mailer_tab.emit('send-target', target)
         yield target
     elif target_type == 'file':
         target_file_h = open(self.target_file, 'rU')
         csv_reader = csv.DictReader(
             target_file_h,
             ('first_name', 'last_name', 'email_address', 'department'))
         for line_no, raw_target in enumerate(csv_reader, 1):
             department = raw_target['department']
             if department is not None:
                 department = department.strip()
                 if department == '':
                     department = None
             email_address = raw_target['email_address'] or ''
             email_address = email_address.strip()
             target = MessageTarget(
                 first_name=raw_target['first_name'].strip(),
                 last_name=raw_target['last_name'].strip(),
                 email_address=email_address,
                 department=department,
                 uid=make_uid(),
                 line=line_no)
             mailer_tab.emit('send-target', target)
             if not target.email_address:
                 self.logger.warning(
                     "skipping line {0} in target csv file due to missing email address"
                     .format(line_no))
                 continue
             if not utilities.is_valid_email_address(target.email_address):
                 self.logger.warning(
                     "skipping line {0} in target csv file due to invalid email address: {1}"
                     .format(line_no, email_address))
                 continue
             yield target
         target_file_h.close()
     else:
         self.logger.error(
             "the configured target type '{0}' is unsupported".format(
                 target_type))
예제 #17
0
	def iterate_targets(self):
		target_type = self.config['mailer.target_type']
		mailer_tab = self.application.main_tabs['mailer']
		if target_type == 'single':
			target_name = self.config['mailer.target_name'].split(' ')
			while len(target_name) < 2:
				target_name.append('')
			target = MessageTarget(
				first_name=target_name[0].strip(),
				last_name=target_name[1].strip(),
				email_address=self.config['mailer.target_email_address'],
				department=None,
				uid=make_uid()
			)
			mailer_tab.emit('send-target', target)
			yield target
		elif target_type == 'file':
			target_file_h = open(self.target_file, 'rU')
			csv_reader = csv.DictReader(target_file_h, ('first_name', 'last_name', 'email_address', 'department'))
			for line_no, raw_target in enumerate(csv_reader, 1):
				if its.py_v2:
					# this intentionally will cause a UnicodeDecodeError to be raised as is the behaviour in python 3.x
					# when csv.DictReader is initialized
					raw_target = dict((k, (v if v is None else v.decode('utf-8'))) for k, v in raw_target.items())
				department = raw_target['department']
				if department is not None:
					department = department.strip()
					if department == '':
						department = None
				email_address = raw_target['email_address'] or ''
				email_address = email_address.strip()
				target = MessageTarget(
					first_name=raw_target['first_name'].strip(),
					last_name=raw_target['last_name'].strip(),
					email_address=email_address,
					department=department,
					uid=make_uid(),
					line=line_no
				)
				mailer_tab.emit('send-target', target)
				if not target.email_address:
					self.logger.warning("skipping line {0} in target csv file due to missing email address".format(line_no))
					continue
				if not utilities.is_valid_email_address(target.email_address):
					self.logger.warning("skipping line {0} in target csv file due to invalid email address: {1}".format(line_no, email_address))
					continue
				yield target
			target_file_h.close()
		else:
			self.logger.error("the configured target type '{0}' is unsupported".format(target_type))
예제 #18
0
    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
예제 #19
0
	def count_emails(self):
		"""
		Count the emails contained in the target CSV file.

		:return: The number of targets in the file.
		:rtype: int
		"""
		targets = 0
		target_file_h = open(self.target_file, 'rU')
		csv_reader = csv.DictReader(target_file_h, ['first_name', 'last_name', 'email_address'])
		for target in csv_reader:
			if not utilities.is_valid_email_address(target['email_address']):
				continue
			targets += 1
		target_file_h.close()
		return targets
예제 #20
0
	def iterate_targets(self, counting=False):
		"""
		Iterate over each of the targets as defined within the configuration.
		If *counting* is ``False``, messages will not be displayed to the end
		user through the notification tab.

		:param bool counting: Whether or not to iterate strictly for counting purposes.
		:return: Each message target.
		:rtype: :py:class:`~.MessageTarget`
		"""
		mailer_tab = self.application.main_tabs['mailer']
		target_type = self.config['mailer.target_type']
		if target_type == 'single':
			target_name = self.config['mailer.target_name'].split(' ')
			while len(target_name) < 2:
				target_name.append('')
			uid_charset = self.config['mailer.message_uid.charset']
			target = MessageTarget(
				first_name=target_name[0].strip(),
				last_name=target_name[1].strip(),
				email_address=self.config['mailer.target_email_address'].strip(),
				uid=utilities.make_message_uid(
					upper=uid_charset['upper'],
					lower=uid_charset['lower'],
					digits=uid_charset['digits']
				)
			)
			if not counting:
				mailer_tab.emit('target-create', target)
			yield target
		elif target_type == 'file':
			for target in _iterate_targets_file(self.target_file, config=self.config):
				missing_fields = target.missing_fields
				if missing_fields:
					if counting:
						msg = "Target CSV line {0} skipped due to missing field{1}".format(target.line, ('' if len(missing_fields) == 1 else 's'))
						msg += ':' + ', '.join(field.replace('_', ' ') for field in missing_fields)
						self.tab_notify_status(msg)
					continue
				if not utilities.is_valid_email_address(target.email_address):
					self.logger.warning("skipping line {0} in target csv file due to invalid email address: {1}".format(target.line, target.email_address))
					continue
				if not counting:
					mailer_tab.emit('target-create', target)
				yield target
		else:
			self.logger.error("the configured target type '{0}' is unsupported".format(target_type))
예제 #21
0
    def count_emails(self):
        """
		Count the emails contained in the target CSV file.

		:return: The number of targets in the file.
		:rtype: int
		"""
        targets = 0
        target_file_h = open(self.target_file, 'rU')
        csv_reader = csv.DictReader(
            target_file_h, ['first_name', 'last_name', 'email_address'])
        for target in csv_reader:
            if not utilities.is_valid_email_address(target['email_address']):
                continue
            targets += 1
        target_file_h.close()
        return targets
예제 #22
0
	def save_alert_settings(self):
		email_address = gui_utilities.gobject_get_value(self.gobjects['entry_email_address'])
		phone_number = gui_utilities.gobject_get_value(self.gobjects['entry_sms_phone_number'])
		sms_carrier = gui_utilities.gobject_get_value(self.gobjects['combobox_sms_carrier'])
		server_user = self.application.server_user
		if email_address and not utilities.is_valid_email_address(email_address):
			gui_utilities.show_dialog_warning('Invalid Email Address', self.parent, 'The email address you have entered is not valid.')
			return
		if phone_number:
			phone_number = ''.join(d for d in phone_number if d in string.digits)
			if len(phone_number) > 11:
				gui_utilities.show_dialog_warning('Invalid Phone Number', self.parent, 'The phone number must not contain more than 11 digits')
				return
		email_address = utilities.nonempty_string(email_address)
		phone_number = utilities.nonempty_string(phone_number)
		sms_carrier = utilities.nonempty_string(sms_carrier)
		self.application.rpc('db/table/set', 'users', server_user.id, ('email_address', 'phone_number', 'phone_carrier'), (email_address, phone_number, sms_carrier))
예제 #23
0
	def iterate_targets(self):
		target_type = self.config['mailer.target_type']
		mailer_tab = self.application.main_tabs['mailer']
		if target_type == 'single':
			target_name = self.config['mailer.target_name'].split(' ')
			while len(target_name) < 2:
				target_name.append('')
			target = MessageTarget(
				first_name=target_name[0].strip(),
				last_name=target_name[1].strip(),
				email_address=self.config['mailer.target_email_address'],
				department=None,
				uid=make_uid()
			)
			mailer_tab.emit('send-target', target)
			yield target
		elif target_type == 'file':
			target_file_h = open(self.target_file, 'rU')
			csv_reader = csv.DictReader(target_file_h, ('first_name', 'last_name', 'email_address', 'department'))
			for line_no, raw_target in enumerate(csv_reader, 1):
				department = raw_target['department']
				if department is not None:
					department = department.strip()
					if department == '':
						department = None
				email_address = raw_target['email_address'] or ''
				email_address = email_address.strip()
				target = MessageTarget(
					first_name=raw_target['first_name'].strip(),
					last_name=raw_target['last_name'].strip(),
					email_address=email_address,
					department=department,
					uid=make_uid(),
					line=line_no
				)
				mailer_tab.emit('send-target', target)
				if not target.email_address:
					self.logger.warning("skipping line {0} in target csv file due to missing email address".format(line_no))
					continue
				if not utilities.is_valid_email_address(target.email_address):
					self.logger.warning("skipping line {0} in target csv file due to invalid email address: {1}".format(line_no, email_address))
					continue
				yield target
			target_file_h.close()
		else:
			self.logger.error("the configured target type '{0}' is unsupported".format(target_type))
예제 #24
0
def count_targets_file(target_file):
	"""
	Count the number of valid targets that the specified file contains. This
	skips lines which are missing fields or where the email address is invalid.

	:param str target_file: The path the the target CSV file on disk.
	:return: The number of valid targets.
	:rtype: int
	"""
	count = 0
	for target in _iterate_targets_file(target_file):
		if target.missing_fields:
			continue
		if not utilities.is_valid_email_address(target.email_address):
			continue
		count += 1
	return count
예제 #25
0
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.loglvl, arguments.logger)

	server_ip = arguments.smtp_server_ip
	target_email = arguments.target_email

	if not utilities.is_valid_ip_address(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.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:')
	for rid in range(len(spf_test.spf_records)):
		record = spf.record_unparse(spf_test.spf_records[rid])
		color.print_status("  #{0} {1: <10} {2}".format(rid + 1, ('(matched)' if rid == spf_test.spf_record_id else ''), record))
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):
		color.print_error('the smtp server ip address specified is invalid')
		return

	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.SPFPermError:
		color.print_error('check_host failed with error: permerror')
		return
	except spf.SPFTempError:
		color.print_error('check_host failed with error: temperror')
		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:')
	for rid in range(len(spf_test.spf_records)):
		record = spf.record_unparse(spf_test.spf_records[rid])
		color.print_status("  #{0} {1: <10} {2}".format(rid + 1, ('(matched)' if rid == spf_test.spf_record_id else ''), record))
예제 #27
0
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))
예제 #28
0
파일: mail.py 프로젝트: l1k3m/king-phisher
	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
예제 #29
0
    def run(self):
        emails_done = 0
        emails_total = self.count_emails()
        max_messages_per_connection = self.config.get(
            'mailer.max_messages_per_connection', 5)
        self.running.set()
        self.should_exit.clear()
        self.paused.clear()
        self._prepare_env()

        self._mime_attachments = self._get_mime_attachments()
        self.logger.debug("loaded {0:,} MIME attachments".format(
            len(self._mime_attachments)))

        target_file_h = open(self.target_file, 'rU')
        csv_reader = csv.DictReader(
            target_file_h, ['first_name', 'last_name', 'email_address'])
        for target in csv_reader:
            if not utilities.is_valid_email_address(target['email_address']):
                self.logger.warning('skipping invalid email address: ' +
                                    target['email_address'])
                continue
            iteration_time = time.time()
            if self.should_exit.is_set():
                self.tab_notify_status('Sending emails cancelled')
                break
            if not self.process_pause():
                break
            if emails_done > 0 and (emails_done % max_messages_per_connection):
                self.server_smtp_reconnect()

            uid = make_uid()
            emails_done += 1
            self.tab_notify_status(
                "Sending email {0:,} of {1:,} to {2} with UID: {3}".format(
                    emails_done, emails_total, target['email_address'], uid))
            msg = self.create_email(target['first_name'], target['last_name'],
                                    target['email_address'], uid)
            if not self._try_send_email(target['email_address'], msg):
                break

            self.tab_notify_sent(emails_done, emails_total)
            campaign_id = self.config['campaign_id']
            company_name = self.config.get('mailer.company_name', '')
            self.rpc('campaign/message/new', campaign_id, uid,
                     target['email_address'], company_name,
                     target['first_name'], target['last_name'])

            if self.max_messages_per_minute:
                iteration_time = (time.time() - iteration_time)
                sleep_time = (60.0 / float(
                    self.max_messages_per_minute)) - iteration_time
                while sleep_time > 0:
                    sleep_chunk = min(sleep_time, 0.5)
                    time.sleep(sleep_chunk)
                    if self.should_exit.is_set():
                        break
                    sleep_time -= sleep_chunk

        target_file_h.close()
        self._mime_attachments = None

        self.tab_notify_status(
            "Finished sending emails, successfully sent {0:,} emails".format(
                emails_done))
        self.server_smtp_disconnect()
        if self.ssh_forwarder:
            self.ssh_forwarder.stop()
            self.ssh_forwarder = None
            self.tab_notify_status('Disconnected from the SSH server')
        self.tab_notify_stopped()
        return
예제 #30
0
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
			))
예제 #31
0
    def run(self):
        emails_done = 0
        emails_total = self.count_messages()
        max_messages_per_connection = self.config.get(
            'mailer.max_messages_per_connection', 5)
        self.running.set()
        self.should_exit.clear()
        self.paused.clear()
        self._prepare_env()

        emails_total = "{0:,}".format(emails_total)
        sending_line = "Sending email {{0: >{0},}} of {1} with UID: {{1}} to {{2}}".format(
            len(emails_total), emails_total)
        emails_total = int(emails_total.replace(',', ''))
        attachments = self.get_mime_attachments()
        self.logger.debug("loaded {0:,} MIME attachments".format(
            sum((len(attachments.files), len(attachments.images)))))

        for target in self.iterate_targets():
            if not utilities.is_valid_email_address(target.email_address):
                if target.email_address:
                    self.logger.warning('skipping invalid email address: ' +
                                        target.email_address)
                else:
                    self.logger.warning('skipping blank email address')
                continue
            iteration_time = time.time()
            if self.should_exit.is_set():
                self.tab_notify_status('Sending emails cancelled')
                break
            if not self.process_pause():
                break
            if emails_done > 0 and (emails_done % max_messages_per_connection):
                self.server_smtp_reconnect()

            uid = make_uid()
            emails_done += 1
            self.tab_notify_status(
                sending_line.format(emails_done, uid, target.email_address))
            msg = getattr(self, 'create_' +
                          self.config['mailer.message_type'])(target, uid,
                                                              attachments)
            if not self._try_send_message(target.email_address, msg):
                break

            self.tab_notify_sent(emails_done, emails_total)
            campaign_id = self.config['campaign_id']
            self.rpc('campaign/message/new', campaign_id, uid,
                     target.email_address, target.first_name, target.last_name,
                     target.department)

            if self.max_messages_per_minute:
                iteration_time = (time.time() - iteration_time)
                sleep_time = (60.0 / float(
                    self.max_messages_per_minute)) - iteration_time
                while sleep_time > 0:
                    sleep_chunk = min(sleep_time, 0.5)
                    time.sleep(sleep_chunk)
                    if self.should_exit.is_set():
                        break
                    sleep_time -= sleep_chunk

        self._mime_attachments = None

        self.tab_notify_status(
            "Finished sending, successfully sent {0:,} messages.".format(
                emails_done))
        self.server_smtp_disconnect()
        if self._ssh_forwarder:
            self._ssh_forwarder.stop()
            self._ssh_forwarder = None
            self.tab_notify_status('Disconnected from the SSH server')
        self.tab_notify_stopped()
        return
예제 #32
0
	def run(self):
		emails_done = 0
		emails_total = self.count_emails()
		max_messages_per_connection = self.config.get('mailer.max_messages_per_connection', 5)
		self.running.set()
		self.should_exit.clear()
		self.paused.clear()
		self._prepare_env()

		emails_total = "{0:,}".format(emails_total)
		sending_line = "Sending email {{0: >{0},}} of {1} with UID: {{1}} to {{2}}".format(len(emails_total), emails_total)
		emails_total = int(emails_total.replace(',', ''))
		self._mime_attachments = self._get_mime_attachments()
		self.logger.debug("loaded {0:,} MIME attachments".format(len(self._mime_attachments)))

		target_file_h = open(self.target_file, 'rU')
		csv_reader = csv.DictReader(target_file_h, ['first_name', 'last_name', 'email_address', 'department'])
		for target in csv_reader:
			if not utilities.is_valid_email_address(target['email_address']):
				if target['email_address']:
					self.logger.warning('skipping invalid email address: ' + target['email_address'])
				else:
					self.logger.warning('skipping blank email address')
				continue
			iteration_time = time.time()
			if self.should_exit.is_set():
				self.tab_notify_status('Sending emails cancelled')
				break
			if not self.process_pause():
				break
			if emails_done > 0 and (emails_done % max_messages_per_connection):
				self.server_smtp_reconnect()

			uid = make_uid()
			emails_done += 1
			self.tab_notify_status(sending_line.format(emails_done, uid, target['email_address']))
			msg = self.create_email(target['first_name'], target['last_name'], target['email_address'], uid)
			if not self._try_send_email(target['email_address'], msg):
				break

			self.tab_notify_sent(emails_done, emails_total)
			campaign_id = self.config['campaign_id']
			department = target['department']
			if department is not None:
				department = department.strip()
				if department == '':
					department = None
			self.rpc('campaign/message/new', campaign_id, uid, target['email_address'], target['first_name'], target['last_name'], department)

			if self.max_messages_per_minute:
				iteration_time = (time.time() - iteration_time)
				sleep_time = (60.0 / float(self.max_messages_per_minute)) - iteration_time
				while sleep_time > 0:
					sleep_chunk = min(sleep_time, 0.5)
					time.sleep(sleep_chunk)
					if self.should_exit.is_set():
						break
					sleep_time -= sleep_chunk

		target_file_h.close()
		self._mime_attachments = None

		self.tab_notify_status("Finished sending emails, successfully sent {0:,} emails".format(emails_done))
		self.server_smtp_disconnect()
		if self._ssh_forwarder:
			self._ssh_forwarder.stop()
			self._ssh_forwarder = None
			self.tab_notify_status('Disconnected from the SSH server')
		self.tab_notify_stopped()
		return
예제 #33
0
	def run(self):
		emails_done = 0
		emails_total = self.count_messages()
		max_messages_per_connection = self.config.get('mailer.max_messages_per_connection', 5)
		self.running.set()
		self.should_stop.clear()
		self.paused.clear()
		self._prepare_env()

		emails_total = "{0:,}".format(emails_total)
		sending_line = "Sending email {{0: >{0},}} of {1} with UID: {{1}} to {{2}}".format(len(emails_total), emails_total)
		emails_total = int(emails_total.replace(',', ''))
		attachments = self.get_mime_attachments()
		self.logger.debug("loaded {0:,} MIME attachments".format(sum((len(attachments.files), len(attachments.images)))))

		for target in self.iterate_targets():
			if not utilities.is_valid_email_address(target.email_address):
				if target.email_address:
					self.logger.warning('skipping invalid email address: ' + target.email_address)
				else:
					self.logger.warning('skipping blank email address')
				continue
			iteration_time = time.time()
			if self.should_stop.is_set():
				self.tab_notify_status('Sending emails cancelled')
				break
			if not self.process_pause():
				break
			if emails_done > 0 and (emails_done % max_messages_per_connection):
				self.server_smtp_reconnect()

			uid = make_uid()
			emails_done += 1
			self.tab_notify_status(sending_line.format(emails_done, uid, target.email_address))
			msg = getattr(self, 'create_' + self.config['mailer.message_type'])(target, uid, attachments)
			if not self._try_send_message(target.email_address, msg):
				break

			self.tab_notify_sent(emails_done, emails_total)
			campaign_id = self.config['campaign_id']
			self.rpc('campaign/message/new', campaign_id, uid, target.email_address, target.first_name, target.last_name, target.department)

			if self.max_messages_per_minute:
				iteration_time = (time.time() - iteration_time)
				sleep_time = (60.0 / float(self.max_messages_per_minute)) - iteration_time
				while sleep_time > 0:
					sleep_chunk = min(sleep_time, 0.5)
					time.sleep(sleep_chunk)
					if self.should_stop.is_set():
						break
					sleep_time -= sleep_chunk

		self._mime_attachments = None

		self.tab_notify_status("Finished sending, successfully sent {0:,} messages.".format(emails_done))
		self.server_smtp_disconnect()
		if self._ssh_forwarder:
			self._ssh_forwarder.stop()
			self._ssh_forwarder = None
			self.tab_notify_status('Disconnected from the SSH server')
		self.tab_notify_stopped()
		return
예제 #34
0
    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)