Skip to content
Snippets Groups Projects
Commit 1cc2b5b1 authored by Tobias Dussa's avatar Tobias Dussa
Browse files

Merge branch 'main' into GPGSig

parents 617598c6 627485df
No related branches found
No related tags found
No related merge requests found
......@@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from email.generator import Generator
from email.message import EmailMessage
from email.utils import formatdate
import argparse
......@@ -27,6 +28,7 @@ import getpass
import hashlib
import jinja2
import os
import magic
import requests
import secrets
import smtplib
......@@ -43,18 +45,20 @@ if sys.version_info < (3, 8):
parser.register('action', 'extend', ExtendAction)
# Parse args
parser.add_argument('-a', '--attach', dest='attach', default=[], nargs=1, action='extend', help='add attachment(s) to mail (default: None)')
parser.add_argument('-b', '--basedir', dest='basedir', default='Mails', help='base directory for all output (default: "Mails")')
parser.add_argument('-B', '--bcc', dest='bcc', default=[], nargs=1, action='extend', help='additional mail recipient to bcc (default: None)')
parser.add_argument('-B', '--bcc', dest='bcc', default=[], nargs=1, action='extend', help='additional mail recipient(s) to bcc (default: None)')
parser.add_argument('-c', '--campaign', dest='campaign', default='Test', help='campaign name (default: "Test")')
parser.add_argument('-C', '--cc', dest='cc', default=[], nargs=1, action='extend', help='additional mail recipient(s) to cc (default: None)')
parser.add_argument('-d', '--dry-run', dest='dryrun', default=False, action='store_true', help='dry run -- do not actually send mails or create targets (default: False)')
parser.add_argument('-f', '--from', dest='sender', default='Nobody <nobody@example.com>', help='sender mail address (default: "Nobody <nobody@example.com>"; implies dry-run if not set)')
parser.add_argument('-F', '--force', dest='force', default=False, action='store_true', help='force insecure login without TLS/SSL (default: False)')
parser.add_argument('-H', '--hashstring', dest='hashstring', default='{salt}{campaign}{infix}-{site}', help='string to be hashed for the URL (default: "{salt}{campaign}{infix}-{site}" where "{salt}" is a random string)')
parser.add_argument('-i', '--input', dest='input', default='{basedir}/{campaign}/Input{infix}.lst', help='input file (default: "{basedir}/{campaign}/Input{infix}.lst")')
parser.add_argument('-o', '--output', dest='output', default='{basedir}/{campaign}/{site}/Mail{infix}.txt', help='output file name template (default: "{basedir}/{campaign}/{site}/Mail{infix}.txt")')
parser.add_argument('-o', '--output', dest='output', default='{basedir}/{campaign}/{site}/Mail{infix}.eml', help='output file name template (default: "{basedir}/{campaign}/{site}/Mail{infix}.eml")')
parser.add_argument('-R', '--reply-to', dest='replyto', default=None, help='reply-to mail address (default: None)')
parser.add_argument( '--salt', dest='salt', default=None, help='salt to use for hashing (default: random 8-byte hex string)')
parser.add_argument( '--sign', dest='sign', default=None, choices=['none', 'gpg', 'smime'], help='signature method (one of "none", "gpg", "smime"; default: "none")')
parser.add_argument( '--sign', dest='sign', default=None, choices=[None, 'gpg', 'smime'], help='signature method (one of "", "gpg", "smime"; default: "")')
parser.add_argument( '--sign-as', dest='sign-as', default=None, help='signature key to use (default: autoselect)')
parser.add_argument('-s', '--subject', dest='subject', default='Security Challenge for {site} -- {campaign}{infix}', help='mail subject (default: "Security Challenge Message -- {campaign}{infix}")')
parser.add_argument('-S', '--smtpserver', dest='smtpserver', default='localhost', help='SMTP server to use (default: "localhost"); port can be specified with "<host>:<port>" notation and takes precedence over implied ports and port specification')
......@@ -65,8 +69,9 @@ parser.add_argument( '--starttls', dest='starttls', default=False, acti
parser.add_argument('-t', '--template', dest='template', default='{basedir}/{campaign}/Mail.template', help='mail template file (default: "{basedir}/{campaign}/Mail.template")')
parser.add_argument('-T', '--to', dest='to', default='{firstname} {lastname} <{email}>', help='recipient mail address (default: "{firstname} {lastname} <{email}>")')
parser.add_argument('-u', '--url', dest='url', default='{webserver}/{campaign}{infix}-{hash}', help='URL template to use (default: "{webserver}/{campaign}{infix}-{hash}"')
parser.add_argument('-U', '--createurl', dest='createurl', default='{webserver}/{campaign}{infix}-{hash}/create', help='URL template to use for creation URL (default: "{webserver}/{campaign}{infix}-{hash}/create"')
parser.add_argument('-v', '--verbose', dest='verbose', default=False, action='store_true', help='increase verbosity')
parser.add_argument('-w', '--webserver', dest='webserver', default='https://challenge.example.com', help='web server to use (default: "https://challenge.example.com"; implies dry-run if not set')
parser.add_argument('-w', '--webserver', dest='webserver', default='https://challenge.example.com', help='web server to use (default: "https://challenge.example.com"; implies dry-run if not set; to suppress web-hook calling, set to empty string ""')
parser.add_argument('infix', default='', nargs='?', help='infix for ID purposes, default empty')
args = parser.parse_args()
......@@ -112,10 +117,14 @@ if not args.salt:
args.salt = secrets.token_hex(8)
if args.verbose:
if args.attach:
print(f'Using "{", ".join(args.attach)}" as mail attachment(s).')
print(f'Using "{args.basedir}" as base directory.')
if args.bcc:
print(f'Using "{", ".join(args.bcc)}" as silent mail recipients.')
print(f'Using "{", ".join(args.bcc)}" as bcc mail recipient(s).')
print(f'Using "{args.campaign}" as campaign.')
if args.cc:
print(f'Using "{", ".join(args.cc)}" as cc mail recipient(s).')
print(f'Using "{args.sender}" as sender mail address.')
if args.replyto:
print(f'Using "{args.replyto}" as reply-to mail address.')
......@@ -127,6 +136,7 @@ if args.verbose:
print(f'Using "{args.template.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as template file.')
print(f'Using "{args.to.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as recipient mail address.')
print(f'Using "{args.url.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as URL template.')
print(f'Using "{args.createurl.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as URL template.')
print(f'Using "{args.webserver}" as web server.')
print(f'Using "{args.infix}" as infix.')
print(f'Using "{args.smtpserver}" as SMTP server.')
......@@ -160,44 +170,65 @@ def toHex(serial):
def createMail(data):
output = args.output.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
directory = os.path.dirname(output)
print(f'Writing mail to {output}')
if not os.path.exists(directory):
os.makedirs(directory)
with open(output, 'wb') as mailfile:
template.stream(data).dump(mailfile, encoding='utf-8')
# Generate email body and attachments
message = EmailMessage()
with open(output, 'r') as mailfile:
message.set_content(mailfile.read())
message.set_content(template.render(data))
for attach in args.attach:
with open(attach, 'rb') as attachment:
message.add_attachment(attachment.read(), *magic.from_file(attach, mime=True).split('/'))
message['Date'] = formatdate(localtime=True)
message['Subject'] = args.subject.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
message['From'] = args.sender.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
message['Reply-To'] = args.replyto.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
if args.replyto:
message['Reply-To'] = args.replyto.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
message['To'] = args.to.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
message['Bcc'] = ', '.join(args.bcc).format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
if args.cc:
message['Cc'] = ', '.join(args.cc).format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
if args.bcc:
message['Bcc'] = ', '.join(args.bcc).format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
# Record mail
print(f'Writing mail to {output}')
if not os.path.exists(directory):
os.makedirs(directory)
with open(output, 'w') as mailfile:
Generator(mailfile).flatten(message)
if not args.dryrun:
# Actually send the mail
print(f'Sending mail to {message["To"]}')
if message['Cc']:
print(f'... and cc to {message["Cc"]}')
if message['Bcc']:
print(f'Sending mail to {message["To"]} (and {message["Bcc"]})')
else:
print(f'Sending mail to {message["To"]}')
if args.starttls:
with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
smtp.starttls()
smtp.login(args.smtpuser, args.smtppass)
smtp.send_message(message)
elif args.smtpuser or args.smtppass:
with smtplib.SMTP_SSL(args.smtpserver, args.smtpport) as smtp:
smtp.login(args.smtpuser, args.smtppass)
smtp.send_message(message)
else:
with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
smtp.send_message(message)
print(f'Creating target at {data["URL"]}/create')
requests.get(f'{data["URL"]}/create')
print(f'... and bcc to {message["Bcc"]}')
try:
if args.starttls:
with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
smtp.starttls()
smtp.login(args.smtpuser, args.smtppass)
smtp.send_message(message)
elif args.smtpuser or args.smtppass:
with smtplib.SMTP_SSL(args.smtpserver, args.smtpport) as smtp:
smtp.login(args.smtpuser, args.smtppass)
smtp.send_message(message)
else:
with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
smtp.send_message(message)
except:
print(f'ERROR sending the mail!')
# Call the webhook if webserver is specified
if args.webserver:
print(f'Creating target at {data["CreateURL"]}')
requests.get(f'{data["CreateURL"]}')
def generateHashAndURL(data):
data['hash'] = hashlib.sha256(args.hashstring.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'])).encode('utf-8')).hexdigest()
data['URL'] = args.url.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash']))
data['CreateURL'] = args.createurl.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash']))
with open(args.input.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt)), 'r') as inputfile:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment