Skip to content

Commit

Permalink
feat: fix some bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
mo-xiaoxi committed Dec 15, 2021
1 parent b826dbb commit 4da1d80
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 85 deletions.
4 changes: 1 addition & 3 deletions config.py
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__))
Expand All @@ -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)
40 changes: 22 additions & 18 deletions config/example.config.yaml
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:
Expand All @@ -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!"
Expand Down Expand Up @@ -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."
Expand All @@ -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."
Expand Down Expand Up @@ -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.


66 changes: 35 additions & 31 deletions core/sender.py
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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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())

Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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):
Expand Down
6 changes: 3 additions & 3 deletions core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ def read_config(config_path):
tmp = func_dict.get(function, functin_call_error)(value)
data = data.replace(k, tmp)
else:
data = data.replace(k, y['global_parameters'][t])
data = data.replace(k, y['default'][t])
config = yaml.safe_load(data)
attack = config['attack']
tp = config['global_parameters']
tp = config['default']

# Complement the default value in attack
for i in iterkeys(attack):
Expand Down Expand Up @@ -92,7 +92,7 @@ def init_log(filename):
)
# formattler = '%(asctime)s %(pathname)-8s:%(lineno)d %(levelname)-8s %(message)s'
# formattler = '%(levelname)-8s %(message)s'
formattler = '[%(levelname)-8s] [%(asctime)s] [%(filename)-8s:%(lineno)-3d] %(message)s'
formattler = '[%(levelname)-7s] [%(asctime)s] [%(filename)-8s:%(lineno)-3d] %(message)s'
fmt = logging.Formatter(formattler)
logger = logging.getLogger()
coloredlogs.install(
Expand Down
99 changes: 69 additions & 30 deletions spoofing.py
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

Expand All @@ -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:
Expand All @@ -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:
Expand Down

0 comments on commit 4da1d80

Please sign in to comment.