-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
130 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
#!/usr/bin/env python | ||
import os,json | ||
import os, json | ||
from core.util import init_log | ||
|
||
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | ||
|
@@ -11,6 +11,4 @@ | |
with open(RULE_PATH, 'r') as f: | ||
CONFIG_RULES = json.load(f) | ||
|
||
DEFAULT_EMAIL = '[email protected]' | ||
|
||
logger = init_log(LOG_FILE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,34 @@ | ||
share_mode: | ||
default: # Set some Global default values | ||
subject: "Template subject" | ||
body: "test" | ||
|
||
|
||
target: #target victim | ||
default: | ||
username: [email protected] # username via SMTP login | ||
password: xxxxxx # password via SMTP login | ||
host: mails.tsinghua.edu.cn # SMTP server | ||
username: '[email protected]' | ||
host: gmail.com # Optional, Target email service domain, e.g. [email protected] ==> 163.com | ||
port: 25 # Optional, default 25 | ||
use_tls: False # Optional, default False | ||
use_ssl: False # Optional, default False | ||
debug_level: False # Optional, default False | ||
|
||
gmail.com: | ||
username: [email protected] | ||
password: xxxx | ||
host: smtp.gmail.com | ||
port: 25 | ||
|
||
|
||
direct_mode: | ||
smtp_user: | ||
default: | ||
host: 163.com # Target email service domain, e.g. [email protected] ==> 163.com | ||
username: [email protected] # username via SMTP login | ||
password: abcdefg # password via SMTP login | ||
host: mails.tsinghua.edu.cn # SMTP server | ||
port: 25 # Optional, default 25 | ||
use_tls: False # Optional, default False | ||
use_ssl: False # Optional, default False | ||
debug_level: False # Optional, default False | ||
|
||
gmail.com: | ||
username: [email protected] | ||
password: xxxx | ||
host: smtp.gmail.com | ||
port: 25 | ||
|
||
|
||
attack: | ||
default: | ||
|
@@ -42,7 +48,7 @@ attack: | |
helo: | ||
autoencode: False | ||
|
||
# A1 attack | ||
# A1 attack | ||
A1: | ||
mail_from: "[email protected]" | ||
subject: "[Warning] Maybe you are vulnerable to the A1 attack!" | ||
|
@@ -81,7 +87,7 @@ attack: | |
# A4 attack | ||
A4: | ||
mime_from: "[email protected]" | ||
extra_headers: {"From": "[email protected]"} | ||
extra_headers: { "From": "[email protected]" } | ||
subject: "[Warning] Maybe you are vulnerable to the A4 attack!" | ||
body: "A4: Multiple From Headers." | ||
description: "A4: Multiple From Headers." | ||
|
@@ -90,7 +96,7 @@ attack: | |
# A5 attack | ||
A5: | ||
mime_from: "<[email protected]>, <[email protected]>" | ||
extra_headers: {"Sender": "[email protected]"} | ||
extra_headers: { "Sender": "[email protected]" } | ||
subject: "[Warning] Maybe you are vulnerable to the A5 attack!" | ||
body: "A5: Multiple From Headers." | ||
description: "A5: Multiple From Headers." | ||
|
@@ -169,7 +175,5 @@ attack: | |
description: "A14: Right-to-left Override Attack in domain." | ||
defense: "You should reject emails which contain these special characters in the sender address or add a warning on UI." | ||
|
||
global_parameters: | ||
subject: "Template subject" | ||
mail_to: "[email protected]" # Change receiveUser to what you like to test. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import smtplib | ||
import smtplib | ||
import time | ||
from config import * | ||
from core.util import * | ||
from config import logger | ||
from core.util import string_types, text_type, query_mx_record | ||
from email import charset | ||
from email.encoders import encode_base64 | ||
from email.mime.base import MIMEBase | ||
|
@@ -19,12 +19,12 @@ def prepare_message(message, sender): | |
if sender.mode == 'share': | ||
message.mime_from = sender.username | ||
else: | ||
message.mime_from = DEFAULT_EMAIL | ||
message.mime_from = '[email protected]' | ||
if message.mail_from is None: | ||
if sender.mode == 'share': | ||
message.mail_from = sender.username | ||
else: | ||
message.mail_from = DEFAULT_EMAIL | ||
message.mail_from = '[email protected]' | ||
if message.mail_to is None: | ||
message.mail_to = message.to_addrs() | ||
assert message.mail_to is not None | ||
|
@@ -82,18 +82,17 @@ def connection(self): | |
|
||
def show_status(self): | ||
status = """ | ||
------------------------------------------------------------------ | ||
Sender config: | ||
Mode: {} | ||
Host: {} | ||
Port: {} | ||
Username: {} | ||
Password: {} | ||
Use_tls: {} | ||
Use_ssl: {} | ||
Debug_level: {} | ||
------------------------------------------------------------------ | ||
""".format(self.mode,self.host,self.port,self.username, self.password,self.use_tls, self.use_ssl,self.debug_level) | ||
Sender Config: | ||
- mode: {} | ||
- host: {} | ||
- port: {} | ||
- username: {} | ||
- password: {} | ||
- use_tls: {} | ||
- use_ssl: {} | ||
- debug_level: {} | ||
""".format(self.mode, self.host, self.port, self.username, self.password, self.use_tls, self.use_ssl, | ||
self.debug_level) | ||
logger.info(status) | ||
return status | ||
|
||
|
@@ -218,8 +217,8 @@ class Message(object): | |
def __init__(self, subject=None, to=None, body=None, html=None, | ||
mime_from=None, cc=None, bcc=None, attachments=None, | ||
reply_to=None, date=None, charset='utf-8', | ||
extra_headers=None, mail_options=None, rcpt_options=None, mail_to=None, mail_from=None, helo=None, | ||
autoencode=None, defense=None, description=None): | ||
extra_headers={}, mail_options=None, rcpt_options=None, mail_to=None, mail_from=None, helo=None, | ||
autoencode=None, defense=None, description=None, sender=None): | ||
self.subject = subject | ||
self.body = body | ||
self.html = html | ||
|
@@ -241,6 +240,7 @@ def __init__(self, subject=None, to=None, body=None, html=None, | |
self.mail_from = mail_from | ||
self.mail_to = mail_to or [] | ||
self.helo = helo | ||
self.sender = sender | ||
# make message_id | ||
self.message_id = make_msgid(domain=self.msg_domain()) | ||
|
||
|
@@ -279,16 +279,13 @@ def validate(self): | |
|
||
def show_status(self): | ||
status = """ | ||
------------------------------------------------------------------ | ||
Envelope: | ||
Helo: {} | ||
Mail From: {} | ||
Mail To: {} | ||
Email Content: | ||
Message Config: | ||
- helo: {} | ||
- mail from: {} | ||
- mail to: {} | ||
- email content: | ||
{} | ||
------------------------------------------------------------------ | ||
""".format(self.helo,self.mail_from,self.mail_to, self.as_string()) | ||
""".format(self.helo, self.mail_from, self.mail_to, self.as_string()) | ||
logger.info(status) | ||
return status | ||
|
||
|
@@ -314,18 +311,20 @@ def as_string(self): | |
msg.attach(alternative) | ||
|
||
msg['Subject'] = Header(self.subject, self.charset) | ||
msg['From'] = self.mime_from | ||
if self.extra_headers: | ||
for key, value in self.extra_headers.items(): | ||
# msg[key] = value | ||
msg.add_header(key, value) | ||
msg['To'] = ', '.join(self.to) | ||
msg['Date'] = formatdate(self.date, localtime=True) | ||
msg['Message-ID'] = self.message_id | ||
msg['From'] = self.mime_from | ||
msg['To'] = ', '.join(self.to) | ||
if self.cc: | ||
msg['Cc'] = ', '.join(self.cc) | ||
if self.reply_to: | ||
msg['Reply-To'] = self.reply_to | ||
if self.sender: | ||
msg['Sender'] = self.sender | ||
for attachment in self.attachments: | ||
f = MIMEBase(*attachment.content_type.split('/')) | ||
f.set_payload(attachment.data) | ||
|
@@ -344,13 +343,18 @@ def as_string(self): | |
f.add_header(key, value) | ||
msg.attach(f) | ||
|
||
# TODO: fix mime_from auto encoding | ||
|
||
s = msg.as_string() | ||
if not self.autoencode: | ||
if not self.autoencode or 'raw' in self.extra_headers: | ||
headers = s.split('\n') | ||
for h in headers: | ||
# fix mime_from auto encoding | ||
if h.startswith('From:'): | ||
s = s.replace(h, "From: {}".format(self.mime_from)) | ||
# add raw data | ||
elif h.startswith("raw:"): | ||
# print(h) | ||
s = s.replace(h, '{}'.format(self.extra_headers['raw'])) | ||
|
||
# # fix run fuzz_test | ||
# for k, v in iteritems(self.run_fuzz): | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,28 @@ | ||
from config import * | ||
from core.util import * | ||
import sys | ||
import traceback | ||
import re | ||
from optparse import OptionParser | ||
from config import logger, CONFIG_PATH | ||
from core.util import read_config, banner | ||
from core.sender import Sender, Message, prepare_message | ||
|
||
#TODO add DKIM signer | ||
|
||
# TODO add DKIM signer | ||
def parse_options(): | ||
parser = OptionParser() | ||
parser.add_option("-m", "--mode", dest="mode", default="s", choices=['s', 'd'], | ||
help="The attack mode with spoofing email (s: Shared MTA, d: Direct MTA)") | ||
parser.add_option("-t", "--target", dest="target", default="default", help="Select target under attack mode.") | ||
parser.add_option("-m", "--mode", dest="mode", default='d', choices=['s', 'd'], | ||
help="The attack mode (s: Shared MTA, d: Direct MTA)") | ||
parser.add_option("-u", "--user", dest="user", default="default", | ||
help="Select smtp_user, only used in Shared MTA mode.") | ||
parser.add_option('-t', "--target", dest='target', default='default', help='Select target victim.') | ||
parser.add_option("-a", "--attack", dest='attack', default="default", | ||
help="Select a specific attack method to send spoofing email.") | ||
parser.add_option("--mail_from", dest='mail_from', default=None, | ||
help='Set Mail From address manually. It will overwrite the settings in config.yaml') | ||
parser.add_option("--mime_from", dest='mime_from', default=None, | ||
help='Set Mime From address manually. It will overwrite the settings in config.yaml') | ||
parser.add_option("--mail_to", dest='mail_to', default=None, | ||
help='Set Mail to address manually. It will overwrite the settings in config.yaml') | ||
parser.add_option("--mime_to", dest='mime_to', default=None, | ||
help='Set Mime to address manually. It will overwrite the settings in config.yaml') | ||
|
||
help="Select one attack method to send spoofing emails.") | ||
parser.add_option("-d", "--debug", dest="debug", action="store_true", help="Turn on debug mode.") | ||
parser.add_option("--list", dest='list', action='store_true', help="list attack methods.") | ||
# parser.add_option('-q', '--quite', action='store_false', dest='quite', help='don\'t print info log.') #TODO | ||
parser.add_option("--mail_from", dest='mail_from', default=None, help='set mail_from address manually.') | ||
parser.add_option("--mime_from", dest='mime_from', default=None, help='set mime_from address manually.') | ||
parser.add_option("--mime_to", dest='mime_to', default=None, help='set mime_to address manually.') | ||
parser.add_option("--mail_to", dest='mail_to', default=None, help='set mail_to address manually.') | ||
(options, args) = parser.parse_args() | ||
return options | ||
|
||
|
@@ -33,28 +33,47 @@ def run_error(errmsg): | |
sys.exit() | ||
|
||
|
||
|
||
def run(): | ||
options = parse_options() | ||
# config | ||
config = read_config(CONFIG_PATH) | ||
check_configs(options, config) | ||
|
||
if options.mode == "s": | ||
target = config["share_mode"][options.target] | ||
target["mode"] = "share" | ||
mail = Sender(**target) | ||
mail.show_status() | ||
if options.list: | ||
show_attacks(config['attack']) | ||
return | ||
demo = None | ||
try: | ||
victim = config['target'][options.target] | ||
except Exception as e: | ||
# Directly fill in the target address, i.e., [email protected] | ||
victim = config['target']['default'] | ||
victim['username'] = options.target.split() | ||
victim['host'] = options.target.split('@')[1].strip() | ||
|
||
if options.mode == "s": | ||
demo = config["smtp_user"][options.user] | ||
demo["mode"] = "share" | ||
elif options.mode == 'd': | ||
target = config['direct_mode'][options.target] | ||
target["mode"] = "direct" | ||
mail = Sender(**target) | ||
mail.show_status() | ||
demo = victim | ||
demo["mode"] = "direct" | ||
else: | ||
logger.error("Option.mode illegal!{}".format(options.mode)) | ||
sys.exit() | ||
errmsg = "Mode setting error! {}".format(options.mode) | ||
run_error(errmsg) | ||
|
||
if options.debug: | ||
demo['debug_level'] = options.debug | ||
|
||
mail = Sender(**demo) | ||
mail.show_status() | ||
|
||
m = config["attack"][options.attack] | ||
try: | ||
m = config["attack"][options.attack] | ||
except Exception as e: | ||
show_attacks(config["attack"]) | ||
sys.exit() | ||
if 'mail_to' not in m or not m['mail_to']: | ||
m['mail_to'] = victim['username'] | ||
if options.mail_from: | ||
m['mail_from'] = options.mail_from | ||
if options.mail_to: | ||
|
@@ -70,6 +89,26 @@ def run(): | |
logger.info("All Task Done! :)") | ||
|
||
|
||
# TODO | ||
def check_configs(options, config): | ||
error = False | ||
errmsg = 'config error' | ||
if error: | ||
run_error(errmsg) | ||
|
||
|
||
|
||
|
||
def show_attacks(attacks): | ||
logger.info("Name\t\t\tDescription\t") | ||
logger.info("-" * 100) | ||
for a in attacks: | ||
description = attacks[a]['description'] if 'description' in attacks[a] else None | ||
# defense = attacks[a]['defense'] if 'defense' in attacks[a] else None | ||
logger.info("{}\t\t\t{}".format(a, description)) | ||
logger.info("-" * 100) | ||
|
||
|
||
def main(): | ||
banner() | ||
try: | ||
|