Skip to content

Commit

Permalink
Overhaul code to 0.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Felipe Molina de la Torre committed Aug 14, 2024
1 parent 4b168d7 commit d26bab0
Show file tree
Hide file tree
Showing 20 changed files with 1,932 additions and 385 deletions.
25 changes: 25 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "Maitm Dev Container",
"dockerFile": "../Dockerfile",
"context": "..",
"build": {
"args": {}
},
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.shell.linux": "/bin/sh"
},
"extensions": [
"ms-python.python",
"ms-azuretools.vscode-docker",
"ms-python.debugpy"
]
}
},
"workspaceFolder": "/Maitm",
"postCreateCommand": "apk add git --no-cache && apk add openssh --no-cache",
"remoteUser": "root",
"workspaceMount": "source=${localWorkspaceFolder},target=/Maitm,type=bind"
}

3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
config/prod
config/*prod*
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,9 @@ config*.yaml
*.pyc
*.old
*.drawio

config/prod/*

attachments/

.DS_Store
26 changes: 19 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
FROM alpine:3.16
FROM alpine:3.20
LABEL name="Maitm"
LABEL "com.example.vendor"="Orange Cyberdefense Sensepost Team"
LABEL org.opencontainers.image.authors="Felipe Molina de la Torre"

COPY *.py /Maitm/
COPY Pipfile* /Maitm/
COPY Pipfile /Maitm/
COPY Maitm /Maitm/Maitm
COPY config /Maitm/config
RUN apk update && \
apk add python3 && \
apk add py3-pip && \
pip install pipenv && \
cd /Maitm && \
pipenv install --python=3.10
# If flags $FORWARD and $NEWONLY are provided, add the flags
apk add gcc && \
apk add python3-dev && \
apk add libc-dev && \
apk add libffi-dev && \
apk add pipx && \
apk add yaml-dev
# Uninstalling setuptools as it produces this error: https://github.com/pypa/setuptools/issues/4483
# It will be installed later during pipenv install command
# Update the path to have pipx tools available in the command line
ENV PATH="$PATH:/root/.local/bin"
RUN pipx install pipenv
WORKDIR /Maitm
# RUN cd /Maitm
RUN pipenv install --python=3.12

# The user has to provide the parameters in Docker invocation, such as:
# docker run --rm -ti maitm -h
# docker run --rm -ti maitm -c config/config.yml -f -n
ENTRYPOINT [ "pipenv", "run", "python", "./mail-in-the-middle.py" ]

10 changes: 6 additions & 4 deletions Maitm/Bells.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ class DiscordBell():
def __init__(self, webhook_url=None) -> None:
self.webhook_url=webhook_url

def ring(self,msg,to,action,reason):
text="===[%s]===\nEmail UID: %s\nFROM: %s\nTO: %s\nSubject: %s\nSize: %s.\nFORWARDED: %s\nREASON: %s" % (msg.date, msg.uid, msg.from_values, to,msg.subject, len(msg.text or msg.html), action, reason)
def ring(self,msg,action,reason):
text="===[%s]===\nEmail UID: %s\nFROM: %s\nTO: %s\nSubject: %s\nFORWARDED: %s\nREASON: %s" \
% (msg["date"], msg["uid"], msg["from"], msg["to"], msg["subject"], action, reason)
webhook = DiscordWebhook(url=self.webhook_url)
embed = DiscordEmbed(title="Message Forwarded", color="03b2f8")
embed.set_description(text)
Expand All @@ -25,10 +26,11 @@ def __init__(self, webhook_url=None) -> None:
def get_teams_webhook(self,url):
return pymsteams.connectorcard(url)

def ring(self,msg,to,action,reason):
def ring(self,msg,action,reason):
webhook=self.get_teams_webhook(self.webhook_url)
webhook.title("Email Forwarded")
text="========[%s]========<br/>Email UID: %s<br/>FROM: %s<br/>TO: %s<br/>Subject: %s<br/>Size: %s.<br/>FORWARDED: %s<br/>REASON: %s" % (msg.date, msg.uid, msg.from_values, to,msg.subject, len(msg.text or msg.html), action, reason)
text="========[%s]========<br/>Email UID: %s<br/>FROM: %s<br/>TO: %s<br/>Subject: %s<br/>FORWARDED: %s<br/>REASON: %s" \
% (msg["date"], msg["uid"], msg["from"], msg["to"], msg["subject"], action, reason)
webhook.text(text)
webhook.send()

Expand Down
128 changes: 128 additions & 0 deletions Maitm/MailConverter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from imap_tools.message import MailMessage as ImapMessage
from email import message_from_string
from email.message import EmailMessage as PythonEmailMessage
from email.mime.image import MIMEImage
from exchangelib import Message as ExchangeMessage, FileAttachment, Account as ExchangeAccount, HTMLBody, Mailbox as ExchangeMailbox
import mimetypes
import base64
import logging.config
import os
import yaml
import email
from email import policy
from email.parser import BytesParser

"""
MailConverter class is used to convert mail from one format to another.
It accepts exchangelib and imap_tools mail objects and converts it to python native email object.
It cannot do the reverse conversion to imap_tools message format, as it has immutable fields and is not needed to send out emails.
"""
class MailConverter:
def __init__(self, exchangelib_mail: ExchangeMessage=None, exchange_account: ExchangeAccount=None, imap_tools_mail=None, clone_cc=False, clone_bcc=False, logfile="logs/mailconverter.log"):
self.init_logging(log_filename=logfile)
self.exchange_account = exchange_account
self.exchange_mail = exchangelib_mail
self.imap_mail = imap_tools_mail
self.python_mail = PythonEmailMessage()
self.clone_cc = clone_cc
self.clone_bcc = clone_bcc

def init_logging(self,log_filename):
# Ensure the 'logs' directory exists
os.makedirs(os.path.dirname(log_filename), exist_ok=True)

# Load the logging configuration from a YAML file
with open('config/logging.yml', 'r') as f:
config = yaml.safe_load(f.read())
# Update the filename in the configuration
config['handlers']['file_handler']['filename'] = log_filename
logging.config.dictConfig(config)

# Example usage
self.logger = logging.getLogger()

"""
This function transforms an imap_tools message to a Python EmailMessage
"""
def convert_from_imapmessage(self, msg: ImapMessage):
raw_email = msg.obj.as_bytes()

# Parse the raw email data
self.python_mail = BytesParser(policy=policy.default).parsebytes(raw_email)
# Add the UID attribute as well from the IMAP library
self.python_mail['uid'] = msg.uid
# Remove the Copied and BCC recipients
del self.python_mail['cc']
del self.python_mail['bcc']
# Remove the 'received' header which may disclose our mail server address
del self.python_mail['Received']
del self.python_mail['Authentication-Results']
del self.python_mail['Delivered-To']
# Remove the 'spam' headers
for header in list(self.python_mail.keys()):
if 'spam' in header.lower():
del self.python_mail[header]

return self.python_mail

"""
This function transforms an exchangelib message to a Python EmailMessage
"""
def convert_from_exchangemail(self, msg: ExchangeMessage):
raw_mime_content = msg.mime_content

# Parse the raw MIME content
self.python_mail = BytesParser(policy=policy.default).parsebytes(raw_mime_content)
self.python_mail['uid'] = msg.id+"-"+msg.changekey
# Remove the Copied and BCC recipients
del self.python_mail['cc']
del self.python_mail['bcc']
# Remove the 'received' header which may disclose our mail server address
del self.python_mail['Received']
del self.python_mail['Authentication-Results']
del self.python_mail['Delivered-To']
# Remove the 'spam' headers
for header in list(self.python_mail.keys()):
if 'spam' in header.lower():
del self.python_mail[header]

return self.python_mail

"""
This function transforms a Python EmailMessage to an exchangelib message format
"""
def convert_to_exchange_message(self, msg: PythonEmailMessage, exchage_account: ExchangeAccount = None):
# Create a new ExchangeMessage object
if exchage_account:
self.exchange_account = exchage_account
if not self.exchange_account:
self.logger.error("Exchange account is not provided")
return None
self.exchange_mail = ExchangeMessage(account=self.exchange_account)

self.exchange_mail.body = HTMLBody(msg.get_body(preferencelist=('html',)).get_content())
self.exchange_mail.subject = msg['Subject']
self.exchange_mail.to_recipients = [ExchangeMailbox(email_address=addr) for addr in msg.get_all('To', [])]
# TODO: Not sure if I can spoof the sender with exchangelib. Proably need to use the account's email address
self.exchange_mail.sender = ExchangeMailbox(email_address=msg['From'])

# Handling CC and BCC if needed
# TODO: Change the CC and Bcc handing
if msg.get_all('Cc', []):
self.exchange_mail.cc_recipients = [ExchangeMailbox(email_address=addr) for addr in msg.get_all('Cc', [])]
if msg.get_all('Bcc', []):
self.exchange_mail.bcc_recipients = [ExchangeMailbox(email_address=addr) for addr in msg.get_all('Bcc', [])]

# Add attachments
for part in msg.iter_parts():
if part.get_content_disposition() == 'attachment':
content_type = part.get_content_type()
maintype, subtype = content_type.split('/')
attachment = FileAttachment(
name=part.get_filename(),
content=part.get_payload(decode=True),
content_type=content_type
)
self.exchange_mail.attach(attachment)

return self.exchange_mail
Loading

0 comments on commit d26bab0

Please sign in to comment.