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 @@ ...@@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from email.generator import Generator
from email.message import EmailMessage from email.message import EmailMessage
from email.utils import formatdate from email.utils import formatdate
import argparse import argparse
...@@ -27,6 +28,7 @@ import getpass ...@@ -27,6 +28,7 @@ import getpass
import hashlib import hashlib
import jinja2 import jinja2
import os import os
import magic
import requests import requests
import secrets import secrets
import smtplib import smtplib
...@@ -43,18 +45,20 @@ if sys.version_info < (3, 8): ...@@ -43,18 +45,20 @@ if sys.version_info < (3, 8):
parser.register('action', 'extend', ExtendAction) parser.register('action', 'extend', ExtendAction)
# Parse args # 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', '--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', '--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('-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', '--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('-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('-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('-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('-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( '--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( '--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', '--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') 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 ...@@ -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', '--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('-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', '--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('-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') parser.add_argument('infix', default='', nargs='?', help='infix for ID purposes, default empty')
args = parser.parse_args() args = parser.parse_args()
...@@ -112,10 +117,14 @@ if not args.salt: ...@@ -112,10 +117,14 @@ if not args.salt:
args.salt = secrets.token_hex(8) args.salt = secrets.token_hex(8)
if args.verbose: if args.verbose:
if args.attach:
print(f'Using "{", ".join(args.attach)}" as mail attachment(s).')
print(f'Using "{args.basedir}" as base directory.') print(f'Using "{args.basedir}" as base directory.')
if args.bcc: 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.') 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.') print(f'Using "{args.sender}" as sender mail address.')
if args.replyto: if args.replyto:
print(f'Using "{args.replyto}" as reply-to mail address.') print(f'Using "{args.replyto}" as reply-to mail address.')
...@@ -127,6 +136,7 @@ if args.verbose: ...@@ -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.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.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.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.webserver}" as web server.')
print(f'Using "{args.infix}" as infix.') print(f'Using "{args.infix}" as infix.')
print(f'Using "{args.smtpserver}" as SMTP server.') print(f'Using "{args.smtpserver}" as SMTP server.')
...@@ -160,44 +170,65 @@ def toHex(serial): ...@@ -160,44 +170,65 @@ def toHex(serial):
def createMail(data): 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'])) 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) directory = os.path.dirname(output)
print(f'Writing mail to {output}')
if not os.path.exists(directory): # Generate email body and attachments
os.makedirs(directory)
with open(output, 'wb') as mailfile:
template.stream(data).dump(mailfile, encoding='utf-8')
message = EmailMessage() message = EmailMessage()
with open(output, 'r') as mailfile: message.set_content(template.render(data))
message.set_content(mailfile.read()) 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['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['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['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['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: 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']: if message['Bcc']:
print(f'Sending mail to {message["To"]} (and {message["Bcc"]})') print(f'... and bcc to {message["Bcc"]}')
else: try:
print(f'Sending mail to {message["To"]}') if args.starttls:
if args.starttls: with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp: smtp.starttls()
smtp.starttls() smtp.login(args.smtpuser, args.smtppass)
smtp.login(args.smtpuser, args.smtppass) smtp.send_message(message)
smtp.send_message(message) elif args.smtpuser or args.smtppass:
elif args.smtpuser or args.smtppass: with smtplib.SMTP_SSL(args.smtpserver, args.smtpport) as smtp:
with smtplib.SMTP_SSL(args.smtpserver, args.smtpport) as smtp: smtp.login(args.smtpuser, args.smtppass)
smtp.login(args.smtpuser, args.smtppass) smtp.send_message(message)
smtp.send_message(message) else:
else: with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp: smtp.send_message(message)
smtp.send_message(message) except:
print(f'Creating target at {data["URL"]}/create') print(f'ERROR sending the mail!')
requests.get(f'{data["URL"]}/create')
# 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): 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['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['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: 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