def test_rating_50(self, f_links, f_settings, f_mail_fields_dict): """Test email with link (which is already in db three times) and username in body_plain""" push_into_db(self.settings1) self.f_link4 = Link(self.link2, 3, 60) push_into_db(self.f_link4) self.mail_fields_dict5 = { "text": " ".join([self.body_plain1, self.username1, self.link2]), "html": "", "subject": self.subject1, "from": self.email1, "from_name": "", "to": [(self.email2, "")], "date": 1587322973.484211, "attachmentFileName": [], "links": [self.link2], } self.mail_fields_dict5["len"] = ( len(self.mail_fields_dict5["html"]) + len(self.mail_fields_dict5["subject"]) + len(self.mail_fields_dict5["text"])) self.mail_fields_dict5["ssdeep"] = get_fuzzy_hash( self.mail_fields_dict5) rating = conclude(self.mail_fields_dict5, self.fake_eml_file, self.fake_mail_request) assert rating == 50
def test_rating_30(self, f_settings, f_mail_fields_dict): """Test email with the username in body_plain, body_plain is very long""" push_into_db(self.settings1) self.body_plain5 = (self.generator.paragraph() + "\n" + self.generator.paragraph()) self.mail_fields_dict5 = { "text": " ".join([self.body_plain5, self.username1]), "html": "", "subject": self.subject1, "from": self.email1, "from_name": "", "to": [(self.email2, "")], "date": 1587322973.484211, "attachmentFileName": [], "links": [], } self.mail_fields_dict5["len"] = ( len(self.mail_fields_dict5["html"]) + len(self.mail_fields_dict5["subject"]) + len(self.mail_fields_dict5["text"])) self.mail_fields_dict5["ssdeep"] = get_fuzzy_hash( self.mail_fields_dict5) rating = conclude(self.mail_fields_dict5, self.fake_eml_file, self.fake_mail_request) assert rating == 30
def prepare_testing_env(): Path("./queue").mkdir(parents=True, exist_ok=True) Path("./queue/new").mkdir(parents=True, exist_ok=True) Path("./undeliverable").mkdir(parents=True, exist_ok=True) Path("./attachments").mkdir(parents=True, exist_ok=True) settings1 = Settings(username="******", password="******") push_into_db(settings1) utils.import_settings(True, boot_module="testing_boot")
def test_rating_100_1(self, f_password): """Test email with the password in body_plain""" self.password4 = "changeme" self.username4 = "changeme" self.settings4 = Settings(username=self.username4, password=self.password4) push_into_db(self.settings4) mail_request = MailRequest(self.eml_file1, None, None, self.eml_content(self.eml_file1)) mail_fields = process_email(self.eml_file1, mail_request) rating = conclude(mail_fields, self.eml_file1, mail_request) assert rating == 100
def test_db_link_read(self, f_links): assert self.f_link1.link == self.link1 for link in self.links_list: push_into_db(link) links = get_links() assert links[0].link == self.f_link1.link assert links[1].link == self.f_link2.link assert links[2].rating == 60 link = get_link_by_name(self.f_link1.link) assert link.counter == 1 update_link_counter(self.f_link2) link = get_link_by_name(self.f_link2.link) assert link.counter == 3
def test_db_settings_read(self, f_settings): for settings in self.settings_list: push_into_db(settings) db_passwords = get_passwords() db_usernames = get_usernames() assert are_credentials_in_db(self.settings1.username, self.settings1.password) assert db_passwords[0] == self.password1 assert db_usernames[0] == self.username1 assert db_passwords[1] == self.password2 assert db_usernames[1] == self.username2 assert db_passwords[2] == self.password3 assert db_usernames[2] == self.username3
def update_statistics(checkpoint_id): """Function changes the statistics about a checkpoint that changes the rating of the email. Args: checkpoint_id (int): Unique id for the checkpoint. """ row = get_statistics_by_checkpoint_id(checkpoint_id) if row: update_statistics_counter(row) else: new_statistics_record = Statistics( checkpoint_id=checkpoint_id, counter=1, created=datetime.timestamp(datetime.today()), last_modified=datetime.timestamp(datetime.today()), ) push_into_db(new_statistics_record)
def f_mail_fields(self, f_mail_fields_dict): self.mail_fields1 = MailFields( subject=self.subject1, email_date=datetime.timestamp(datetime.today()), body_html=self.body_html1, ssdeep=get_fuzzy_hash(self.mail_fields_dict1), length=self.length(self.mail_fields_dict1), attachment=False, ) self.mail_fields2 = MailFields( subject=self.subject2, email_date=datetime.timestamp(datetime.today()), body_plain=self.body_plain2, ssdeep=get_fuzzy_hash(self.mail_fields_dict2), length=self.length(self.mail_fields_dict2), attachment=False, ) self.mail_fields3 = MailFields( subject=self.subject3, email_date=datetime.timestamp(datetime.today()), body_plain=self.body_plain3, body_html=self.body_html3, ssdeep=get_fuzzy_hash(self.mail_fields_dict3), length=self.length(self.mail_fields_dict3), attachment=True, ) self.mail_fields4 = MailFields( subject=self.subject4, email_date=datetime.timestamp(datetime.today()), body_plain=self.body_plain4, body_html=self.body_html4, ssdeep=get_fuzzy_hash(self.mail_fields_dict4), length=self.length(self.mail_fields_dict4), attachment=False, ) self.mail_fields_list = [ self.mail_fields1, self.mail_fields2, self.mail_fields3, self.mail_fields4, ] for mf in self.mail_fields_list: push_into_db(mf)
def test_rating_55(self, f_settings, f_mail_fields_dict): """Test email with the username in body_plain and test time""" push_into_db(self.settings1) self.mail_fields_dict5 = { "text": " ".join([self.body_plain1, self.username1]), "html": "", "subject": self.subject1, "from": self.email1, "from_name": "", "to": [(self.email2, "")], "date": 1587298372.484211, "attachmentFileName": [], "links": [], } self.mail_fields_dict5["len"] = ( len(self.mail_fields_dict5["html"]) + len(self.mail_fields_dict5["subject"]) + len(self.mail_fields_dict5["text"])) self.mail_fields_dict5["ssdeep"] = get_fuzzy_hash( self.mail_fields_dict5) rating = conclude(self.mail_fields_dict5, self.fake_eml_file, self.fake_mail_request) assert rating == 55
def test_rating_0(self, f_settings, f_mail_fields_dict): """Test email with attachment and word test in subject""" push_into_db(self.settings1) self.mail_fields_dict5 = { "text": " ".join([self.body_plain2]), "html": "", "subject": " ".join(["test"]), "from": self.email1, "from_name": "", "to": [(self.email2, "")], "date": 1587322973.484211, "attachmentFileName": ["test.doc"], "links": [], } self.mail_fields_dict5["len"] = ( len(self.mail_fields_dict5["html"]) + len(self.mail_fields_dict5["subject"]) + len(self.mail_fields_dict5["text"])) self.mail_fields_dict5["ssdeep"] = get_fuzzy_hash( self.mail_fields_dict5) rating = conclude(self.mail_fields_dict5, self.fake_eml_file, self.fake_mail_request) assert rating == 0
def conclude(mail_fields, key, mail_request): """This is a key function of the whole relay part. Function calls other auxiliary functions and calculates the final rating of the email. This is called each time a new email arrives. Args: mail_fields (dict): Email parsed in the dictionary. key (str): Name of the file picked up from queue/new. mail_request (MailRequest): Instance of the MailRequest class with eml data. Returns: int: Final email rating when the environmental variable SALMON_SETTINGS_MODULE is set. """ logging.debug( "[+] (salmonconclude.py) - In conclude, started calculating the final spam rating." ) attachment = False if mail_fields["attachmentFileName"]: attachment = True # just for the testing purpose if mail_fields["text"] == "this is testing email from salmon": salmonrelay.relay(mail_fields, key, mail_request, 100) return 100 spam = Spam( subject=str(mail_fields["subject"]), email_date=str(mail_fields["date"]), body_plain=mail_fields["text"], body_html=mail_fields["html"], ssdeep=mail_fields["ssdeep"], length=mail_fields["len"], attachment=attachment, ) database_mail_fields = MailFields( subject=spam.subject, email_date=spam.email_date, body_plain=spam.body_plain, body_html=spam.body_html, ssdeep=spam.ssdeep, length=spam.length, attachment=spam.attachment, ) sender = Sender(mail_fields["from"], mail_fields["from_name"], database_mail_fields) recipient_model_list = spam.get_recipients_for_db( mail_fields["to"], database_mail_fields ) # 1.check - try to find password in email spam.find_password_in_email() # 2.check - try to find username in email usernames = get_usernames() spam.find_username_in_email(usernames) # 3.check - check if the email contains links links_from_db = None links_into_db = None if len(mail_fields["links"]) > 0: links_from_db = get_links() links_into_db = spam.get_links_for_db(mail_fields["links"], links_from_db) # 4.check - verify that the email contains an attachment if len(mail_fields["attachmentFileName"]) > 0: spam.rating -= 10 if utils.settings.data["relay"]["save_statistics"]: update_statistics(1) # 5.check - try to find the word test or testing in the email spam.find_word_test_in_email() # 6.check - verify that the recipient has already been used in test emails is_recipient_in_testmail = spam.is_recipient_in_testmail(mail_fields["to"]) # 7.check - check the time the email arrived spam.investigate_time() # 8.check - try to find the IP address of the honeypot in the email spam.find_ip_address_in_email() # 9.check - analyze what is written in the email # this is optional and has to be set in the salmon.yaml if utils.settings.data["relay"]["analyze_text"]: analyze_text(mail_fields, spam) # 10.check - verify that similar email is not already in the database tables = get_tables_from_similar(spam) # there is a very high probability that email is testing push_into_db_testing( tables, spam, database_mail_fields, recipient_model_list, sender ) # Verify that current email doesn't look more like a testing one can_push_into_db = False if len(tables) > 0: updater = DBUpdater(spam.rating, mail_fields, usernames) for table in tables: if isinstance(table, MaybeTestMail): if updater(table): can_push_into_db = True # there is a chance that email is testing but not for sure push_into_db_maybetesting( tables, spam, can_push_into_db, database_mail_fields, recipient_model_list, sender, ) # similar is in the maybe_test_emails table, but this one has a high rating for table in tables: if isinstance(table, MaybeTestMail) and ( spam.rating >= 70 or is_recipient_in_testmail ): logging.info( "[+] (salmonconclude.py) - Moving a record from the maybe_test_emails table to the test_emails table." ) move_to_testmail(table.mail_fields_id) if is_recipient_in_testmail: recipient_in_testmail(spam) if len(mail_fields["links"]) > 0: spam.update_link_rating(mail_fields["links"], links_from_db) if links_into_db and spam.rating >= 50: for link in links_into_db: link_model = Link(link, 1, spam.rating) push_into_db(link_model) # 11.check - match the email against the rule file # this is optional and has to be set in the salmon.yaml if utils.settings.data["relay"]["use_rule_file"]: for rule in utils.settings.rules: if spam.match_against_rule_file(rule): logging.info( "[+] (salmonconclude.py) - Email successfully matched against the rule %s" % rule["name"] ) spam.rating = 100 final_rating = spam.rating del spam if "SALMON_SETTINGS_MODULE" in os.environ: return final_rating salmonrelay.relay(mail_fields, key, mail_request, final_rating)
def test_db_recipient_read(self, f_recipient): for recipient in self.recipients_list: push_into_db(recipient) db_recipients = get_recipients() assert db_recipients[3].email == self.email1 assert db_recipients[4].name == self.name2
def test_db_test_mail_read(self, f_test_emails): for email in self.test_emails_list: push_into_db(email) email = get_testmail_by_mailfield_id(1) assert email.id == 1 assert email.mailfield.subject == self.subject1
def test_db_maybe_test_mail_read(self, f_maybe_test_emails): for email in self.maybe_test_emails_list: push_into_db(email) email = get_maybetestmail_by_mailfield_id(2) assert email.id == 2 assert email.mailfield.subject == self.subject2