send_email.py : ajouter une sortie JSON
Faire un sorte que le script puisse fonctionner en mode non interactif : dans ce cas, sa sortie sur la console est du JSON. Cela permet ensuite à une éventuelle API REST de facilement se pluguer dessus.
This commit is contained in:
parent
cacba346f6
commit
1e14402a92
|
@ -1,8 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from argparse import ArgumentParser
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
import email.utils
|
import email.utils
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
import smtplib
|
import smtplib
|
||||||
|
@ -61,15 +63,18 @@ custom_theme = Theme({
|
||||||
"data_display_name": "medium_purple1",
|
"data_display_name": "medium_purple1",
|
||||||
"comment": "#777777",
|
"comment": "#777777",
|
||||||
"failure": "bold red",
|
"failure": "bold red",
|
||||||
"success": "green"
|
"success": "green",
|
||||||
|
"text": "default"
|
||||||
})
|
})
|
||||||
console = Console(highlighter=None, theme=custom_theme)
|
console = Console(highlighter=None, theme=custom_theme)
|
||||||
|
|
||||||
|
|
||||||
class SMTP(smtplib.SMTP):
|
class SMTP(smtplib.SMTP):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, output, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.highlighter = SMTPHighlighter()
|
self.highlighter = SMTPHighlighter()
|
||||||
|
self.log = [];
|
||||||
|
self.output = output
|
||||||
|
|
||||||
def trace(self, s, direction):
|
def trace(self, s, direction):
|
||||||
if direction not in ['in', 'out']:
|
if direction not in ['in', 'out']:
|
||||||
|
@ -80,27 +85,33 @@ class SMTP(smtplib.SMTP):
|
||||||
|
|
||||||
if isinstance(s, bytes):
|
if isinstance(s, bytes):
|
||||||
s = s.decode('utf-8')
|
s = s.decode('utf-8')
|
||||||
lines = [self.highlighter(rich.text.Text(line)) for line in s.splitlines()]
|
|
||||||
|
lines = [{'class': 'text', 'text': text} for text in s.splitlines()]
|
||||||
|
|
||||||
|
# lines = [self.highlighter(rich.text.Text(line)) for line in s.splitlines()]
|
||||||
if len(lines) > max_lines:
|
if len(lines) > max_lines:
|
||||||
suppressed_count = len(lines[max_lines:-1])
|
suppressed_count = len(lines[max_lines:-1])
|
||||||
suppressed_text_obj = rich.text.Text(f"({suppressed_count} ")
|
|
||||||
if suppressed_count > 1:
|
if suppressed_count > 1:
|
||||||
suppressed_text_obj.append("lignes omises")
|
suppressed_text = f"{suppressed_count} lignes omises"
|
||||||
else:
|
else:
|
||||||
suppressed_text_obj.append("ligne omise")
|
suppressed_text = f"{suppressed_count} ligne omise"
|
||||||
suppressed_text_obj.append(")")
|
suppressed_obj = {'class': 'comment', 'text': suppressed_text}
|
||||||
suppressed_text_obj.stylize("comment")
|
|
||||||
|
|
||||||
lines = lines[:max_lines] + [suppressed_text_obj] + lines[-1:]
|
lines = lines[:max_lines] + [suppressed_obj] + lines[-1:]
|
||||||
|
|
||||||
|
# Print the text on the console
|
||||||
|
if self.output:
|
||||||
for i, l in enumerate(lines):
|
for i, l in enumerate(lines):
|
||||||
prefix = arrows[direction] if i == 0 else " "
|
prefix = arrows[direction] if i == 0 else " "
|
||||||
console.print(f" {prefix} ", end=None)
|
console.print(f" {prefix} ", end=None)
|
||||||
console.print(l)
|
console.print(l['text'], style=l['class'])
|
||||||
|
|
||||||
if direction == 'in':
|
if direction == 'in':
|
||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
# Save it internally too
|
||||||
|
self.log.append({'direction': direction, 'lines': lines})
|
||||||
|
|
||||||
def putcmd(self, cmd, args=''):
|
def putcmd(self, cmd, args=''):
|
||||||
super().putcmd(cmd.upper(), args)
|
super().putcmd(cmd.upper(), args)
|
||||||
|
|
||||||
|
@ -119,7 +130,9 @@ class SMTP(smtplib.SMTP):
|
||||||
return errcode, errmsg
|
return errcode, errmsg
|
||||||
|
|
||||||
def connect(self, *args, **kwargs):
|
def connect(self, *args, **kwargs):
|
||||||
console.print(" * Connexion établie au serveur", style="comment")
|
self.log.append({'direction': 'comment',
|
||||||
|
'lines': [{'class': 'comment',
|
||||||
|
'text': 'Connexion établie au serveur'}]})
|
||||||
return super().connect(*args, **kwargs)
|
return super().connect(*args, **kwargs)
|
||||||
|
|
||||||
def ehlo(self, *args, **kwargs):
|
def ehlo(self, *args, **kwargs):
|
||||||
|
@ -218,21 +231,20 @@ def ask_template():
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def send_email(helo, envelope_from, email):
|
def send_email(helo, envelope_from, email, output=True):
|
||||||
data = email.as_string().encode('utf-8')
|
data = email.as_string().encode('utf-8')
|
||||||
|
with SMTP(local_hostname=helo, output=output) as smtp:
|
||||||
try:
|
try:
|
||||||
with SMTP(local_hostname=helo) as smtp:
|
|
||||||
smtp.connect(VICTIM_MX)
|
smtp.connect(VICTIM_MX)
|
||||||
smtp.sendmail(envelope_from, VICTIM_ADDRESS, data)
|
smtp.sendmail(envelope_from, VICTIM_ADDRESS, data)
|
||||||
|
return {'outcome': 'success', 'log': smtp.log}
|
||||||
except smtplib.SMTPException:
|
except smtplib.SMTPException:
|
||||||
console.print(" [failure]✘[/] Message [failure]rejeté[/] par le serveur SMTP")
|
return {'outcome': 'failure', 'log': smtp.log}
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
else:
|
|
||||||
console.print(" [success]✔[/] Message [success]accepté[/] par le serveur SMTP")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def interactive_main():
|
||||||
try:
|
try:
|
||||||
console.print(
|
console.print(
|
||||||
rich.panel.Panel(
|
rich.panel.Panel(
|
||||||
|
@ -256,7 +268,12 @@ def main():
|
||||||
email = generate_email(template)
|
email = generate_email(template)
|
||||||
signed_email = add_dkim_signature(email)
|
signed_email = add_dkim_signature(email)
|
||||||
|
|
||||||
send_email(helo, envelope_from, signed_email)
|
results = send_email(helo, envelope_from, signed_email, output=True)
|
||||||
|
if results['outcome'] == 'success':
|
||||||
|
console.print(" [success]✔[/] Message [success]accepté[/] par le serveur SMTP")
|
||||||
|
elif results['outcome'] == 'failure':
|
||||||
|
console.print(" [failure]✘[/] Message [failure]rejeté[/] par le serveur SMTP")
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
console.print()
|
console.print()
|
||||||
pass
|
pass
|
||||||
|
@ -264,5 +281,44 @@ def main():
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument('--non-interactive', default=False, action='store_true')
|
||||||
|
parser.add_argument('--get-config', default=False, action='store_true')
|
||||||
|
parser.add_argument('--template')
|
||||||
|
parser.add_argument('--replace-rfc5321-mail-from', default=False, action='store_true')
|
||||||
|
parser.add_argument('--helo', default=DEFAULT_HELO)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.non_interactive:
|
||||||
|
if args.get_config:
|
||||||
|
config = {
|
||||||
|
'default_helo': DEFAULT_HELO,
|
||||||
|
'my_mail_from': ATTACKER_MAIL_FROM,
|
||||||
|
'templates': [{'id': t.name,
|
||||||
|
'name': t.nice_name,
|
||||||
|
'from_name': re.match('(.*) <(.*)>', t.msg['From']).group(1),
|
||||||
|
'from_address': re.match('(.*) <(.*)>', t.msg['From']).group(2)}
|
||||||
|
for t in spoof_templates.spoof_templates()]
|
||||||
|
}
|
||||||
|
|
||||||
|
print(json.dumps(config))
|
||||||
|
else:
|
||||||
|
templates = {t.name: t for t in spoof_templates.spoof_templates()}
|
||||||
|
template = templates[args.template]
|
||||||
|
rfc5322from, = re.match('.* <(.*)>', template.msg['From']).groups()
|
||||||
|
if args.replace_rfc5321_mail_from:
|
||||||
|
envelope_from = ATTACKER_MAIL_FROM
|
||||||
|
else:
|
||||||
|
envelope_from = rfc5322from
|
||||||
|
|
||||||
|
email = generate_email(template)
|
||||||
|
results = send_email(args.helo, envelope_from, email, output=False)
|
||||||
|
print(json.dumps(results))
|
||||||
|
else:
|
||||||
|
interactive_main()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in New Issue