We recently evaluated the security posture of a public-facing kiosk system. The primary objective was to identify exploitable vulnerabilities in the device’s implementation (a locked-down Windows 10 PC) and any third-party software installed on it. The device was managed by an internal IT team using Ivanti Endpoint Manager (EPM).
Endpoint Manager is an on-premises solution for remotely managing an organization’s edge devices (user workstations, IoT systems, etc.). The motivation to test in the first place was due to the number of recently released CVEs affecting Endpoint Manager and other products documented in Ivanti’s Security Advisory Blog since May of 2024. Ivanti has taken steps to improve its secure coding processes and signed CISA’s Secure by Design pledge.
We knew vulnerabilities existed in the client’s deployment of Endpoint Manager, as they were running Endpoint Manager 2020.1 which is now end-of-life. While we did verify some known CVEs were exploitable in the client’s deployment of Endpoint Manager, we did not expect to discover another new vulnerability in the EPM agent running on the device.
Ivanti EPM Agent < 2020.1 SU6 Windows Local Privilege Escalation
While looking for vulnerabilities in the EPM agent, we enumerated the files, processes, and network sockets created by the software on the device. We discovered the process residentAgent.exe
running in the Local System (Administrator) context, exposing several ports, including localhost:9592
.
We sent arbitrary HTTP requests to http://localhost:9592
and realized this service is how the device communicates with the Endpoint Management Core server located in the internal client network. The agent used a private key stored on the device to send certificate-authenticated requests through the Ivanti Cloud Services Appliance (CSA) hosted externally, which then forwarded the requests to the Core. When the Core server’s hostname was supplied in the HTTP request, the request would be proxied.
We found a file C:\\\\Program Files (x86)\\LANDesk\\Shared Files\\proxyhost.log
that contained logs of each request being sent to port 9592, generated by a program invoked by the EPM agent named proxyhost.exe
. For each request sent to http://localhost:9592
, proxyhost.log
contained a log entry with a variable bRequestIdForCore
. If the Host header contained the domain of the EPM Core, this value was set to 1, and the request was sent to the CSA to be proxied to the Core.
The value was set to 0 for all other requests and sent directly to the location specified in the Host header without certificate authentication.
Understanding that the program logic utilized the Host header to determine the routing of the request, we attempted an incredibly advanced hacking technique. We inserted a double quote in the Host header. No response was received, and the Proxyhost logs showed an unterminated string error in the program.
The error log revealed that the program utilized the Microsoft JScript scripting engine, an archaic JavaScript engine for Windows that was released in 1996. We then sent "+"
to close the unterminated string, and no error was generated, indicating JavaScript code could be injected.
If we could get the EPM agent to run custom code, we could escalate our privileges to Administrator. To do that, we would need to figure out how to execute system commands using Microsoft JScript. Rather than digging through search results for the solution, we consulted the oracle.
We supplied the following code in the Host header to verify the code injection vulnerability.
"+(new ActiveXObject("WScript.Shell")).Run("powershell -c whoami > proof.txt")+"
A proof.txt file was created in the Local System home directory, C:\\Windows\SysWOW64
, and the text NT AUTHORITY\SYSTEM
was inside, indicating the whoami
command had been run with Administrative privileges.
We investigated the vulnerable function FindProxyForURL
in the decompiled code of proxyhost.exe
and noticed the parameters were inserted directly into the function call, creating the code injection vulnerability.
Researching the function name revealed this is the same function that exists in every Proxy Auto-Configuration (PAC) script. PAC scripts are JavaScript code files that can customize the proxy server used by a machine on a per-request basis rather than using one proxy server for all traffic. The proxyhost.exe
program appeared to retrieve the PAC script currently configured for the current user and honor its prescriptions when deciding where to forward incoming requests. A PAC script was enabled for the machine when the vulnerability was first exploited and was used to block browser traffic to anywhere but the clinic site.
Disabling the PAC script blocked the exploit – identical payloads resulted in empty responses, and no runtime errors could be seen in the proxyhost.exe
logs. The exploit was possible again after re-enabling the PAC script.
We changed the PAC script address to a custom script and tested the exploit with various scripts enabled. As long as the PAC script’s syntax was valid, any script contents would make the exploit possible. Additionally, the JavaScript code embedded in the PAC script could be injected into the program’s execution. We added the original proof-of-concept code to the FindProxyForURL
function in the PAC script, which executed whoami
.
function FindProxyForURL(url, host) {
var shell = new ActiveXObject("WScript.Shell");
shell.Run("powershell -c whoami > C:\\\\Users\\Clinic_0000\\Desktop\\admin.txt");
}
Enterprises commonly utilize PAC scripts to configure the traffic of end-user devices. If one is not employed, and the proxy settings are locked using Windows Group policy, system administrators would need to explicitly lock the registry key ‘HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings
‘ to prevent an attacker from enabling a PAC script to exploit the issue (which is unlikely).
Despite this unusual but easily replicable condition, this privilege escalation vulnerability is relatively trivial. The full proof-of-concept of the exploit can be found here.
Conclusion
This vulnerability demonstrates that some bugs can be trivial to enumerate and exploit with a sufficient understanding of how the software processes user input. However, it’s not always easy to have the opportunity to find these issues. Ivanti Endpoint Manager is commercial off-the-shelf (COTS) software that requires a valid subscription to access and employ. Without purchasing commercial software or having (legal) access to a customer system, there is no way for the broader security community to research these vulnerabilities, unlike open source or freeware.
Luckily, Ivanti has a robust Vulnerability Disclosure Program, which includes a legal safe harbor for researchers who find security flaws in their products and disclose them to Ivanti in good faith. Even better, Ivanti operates a private bug bounty program on several of its product lines, a significant reason they have discovered and patched many issues in the past year.
After we reported this issue to Ivanti, they confirmed they were unaware and that the proof-of-concept was valid. However, no CVE was awarded due to the software being end-of-life. So the real question is, how many easily exploitable, unknown vulnerabilities are present in COTS software that’s not regularly tested for security issues and broadly available? Even worse, how many vulnerabilities are present in the outdated commercial software that you have delayed upgrading despite having no released CVEs? Let this be a warning: Beware of the skeletons in your legacy systems – patch regularly and test often!
Share via: