Ivanti EPMM Exploitation: Hit-and-Run
by Bert Steppe
Strategic Threat Intelligence & Research Group (STINGR)
25/03/2026
Executive Summary
This blogpost, written by WithSecure's STINGR Group, presents the analysis of a security incident that happened in February 2026 and was investigated by the WithSecure Incident Response team. Threat actors exfiltrated sensitive data from a server running Ivanti Endpoint Manager Mobile (EPMM) by exploiting two critical RCE vulnerabilities for which a public Proof-of-Concept exploit had appeared only a few days before this incident.
Although exploitation of these vulnerabilities was widespread in-the-wild and has since been described in a number of write-ups, the investigation of this IR case led to several previously undocumented key findings:
- We observed opportunistic exploitation attempts around the same time by multiple threat actors. Some of these attemps were successful (and managed to create persistent access to be used in the future), others failed due to URL encoding errors.
- The attack in which data exfiltration was confirmed, has likely been conducted in a fully automatic way and was well prepared.
- This threat actor used tooling based on an open-source offensive framework known as AntSword, which reduced the time and effort to develop a working attack. Moreover, there are signs of reusing parts of the tooling from another threat actor.
Introduction
Since late January, threat actors have been actively exploiting CVE-2026-1281 and CVE-2026-1340, two critical RCE vulnerabilities in Ivanti Endpoint Manager Mobile (EPMM), a platform used by enterprises to manage mobile devices. The CVEs allow an unauthenticated attacker to execute arbitrary code on the system hosting the affected software.
A detailed technical analysis of the vulnerabilities can be found in other write-ups, but in summary, exploitation can be achieved by sending a specially crafted HTTP GET request of the following format:
If the start time ('st') or end time ('et') is set to 'theValue ', the server logic enters an unexpected state. This will cause any backquoted strings in the 'gPath' array to be evaluated by the shell, enabling pre-authentication code execution.
Before Ivanti released its official security advisory, the vulnerabilities were already exploited as zero-days at a small number of customer environments. However, mass exploitation started shortly after a public Proof-of-Concept became available.
One of the compromised instances was investigated by WithSecure's Incident Response (IR) team, who contacted the Strategic Threat Intelligence & Research Group (STINGR) to assist with the analysis of the web server HTTP access logs that indicated exploitation attempts.
This blogpost describes the results of that analysis, and includes some findings that - to our knowledge - have not been previously reported.
The "trial-and-error" exploitation
On the investigated host, we identified the execution of commands that were also observed at other vulnerable EPMM instances:
1. Reconnaisance: the attacker sends a sleep call in the request, and if the connection hangs for the specified duration, the attacker could confirm that the target is vulnerable.
2. Attempt to execute a reverse shell by initiating an outbound connection from the server.
3. Webshell: create persistent access by appending a small Java-based webshell to the '403.jsp' error page.
The command contains a base64 encoded string that will be decoded and executed using 'bash':
The decoded command first checks whether the webshell already exists in '403.jsp'. If not, it will be appended. Additionally, the command sets the 'setuid' bit to '/usr/bin/env', allowing the threat actor to execute commands with 'root' privileges.
Figure 05: Base64-decoded payload (formatted, embedded 'echo' string shortened)
The webshell checks if the HTTP request has a parameter named 'k'. If so, the value is expected to be a compiled Java class, in base64-encoded form. It implements two ways of decoding the payload to support older and newer versions of the Java runtime.
Figure 06: Base64-decoded and formatted content added to '403.jsp'
In other words, this allows the execution of any Java code that an attacker compiles and encodes, essentially acting as a potential backdoor and thus providing persistent access to the compromised host.
This webshell is a variant of the open-source JSP webshell known as AntSword. The only relevant difference is that a couple of names have been "obfuscated": it checks for argument 'k' instead of 'ant' in the HTTP request, and the method 'base64Decode' has been renamed to simply 'p'.
All of the above commands were executed over several days, starting on 2 February, and with numerous variations. This indicates that multiple threat actors were mass scanning the internet for vulnerable EPMM servers.
It should also be noted that not every command was properly executed due to incorrect URL encoding, which suggests the highly opportunistic nature of these attacks.
A threat actor with a mission
However, on 9 February, a sequence of HTTP requests was sent by an attacker who appeared to know exactly what they were doing. The entire operation was completed within just 6 seconds, strongly suggesting that it was fully automated and leaving no opportunity for manual intervention.
The first request triggered the vulnerability to install the webshell in '403.jsp'.
This request was repeated 10 times, with identical content except for the random UUID in the IPA filename. While the rationale behind this repetition is unclear, our hypothesis is that the attacker's tooling interpreted the response value '1' (returned when the webshell already exists, as shown in Figure 05) as a failure and therefore retried the exploit up to a fixed limit. In this case, the webshell had already been added by another threat actor on 4 February.
It is important to note that this initial command does not execute anything else by itself: it just adds the "feature" to '403.jsp' to accept and load a Java class passed via the 'k' argument in the HTTP request. That is exactly how the threat actor used it in the subsequent HTTP request:
The other argument, 'k0f53cf964d387', is ignored by the web shell, but it is used by the loaded Java class, as shown in Figure 09 below.
As mentioned before, the webshell base64-decodes the 'k' parameter, resulting in a binary with header bytes 'CA FE BA BE', a compiled Java class. We decompiled the payload using jdec:
Figure 09: Decompiled 'Info' class payload
The webshell code in the JSP file loads the 'Info' class and executes the 'equals()' method, which performs two actions:
- Collect some basic system information: the user working directory, the operating system name, the user account name, and the file system root paths. Concatenate the values into a tab-separated string.
- Get the 'k0f53cf964d387' argument, remove the first two characters, and base64-decode the remaining string. The result should be a next-stage compiled Java class. Instantiate the class, and invoke the constructor that accepts a String as the first and only argument. The collected system information is passed as the constructor argument, so that this next-stage payload can use it. Finally, call the 'toString()' method of this class and return its result in the HTTP response. If any error occurs, or if no next-stage payload is provided, then return the original string of system information in the response. The returned data is always wrapped in fixed marker strings '3cd3d' and 'e60537'.
In this case, the 'k0f53cf964d387' argument is just two characters 'WX', which is removed, so the payload simply returned the original string of system information. In other words, the attacker has only used this payload for reconnaissance purposes.
This is the same payload observed in some other attacks targeting EPMM servers.
Just like the webshell itself, there is again a significant code overlap with an AntSword webshell payload. The main differences are also some names and constants, which are more obfuscated than in the AntSword version.
The unobfuscated AntSword payload reveals an interesting detail though: the parameter checked in the HTTP request is called 'antswordargdecoder'. We suspect that the AntSword authors did not have a next-stage payload feature in mind here, but rather a way to customize the formatting of the collected information.
Regardless, the attacker could have used this payload feature to execute a next-stage payload, but they went for a different approach. Here is what they sent next:
Figure 10: The other HTTP requests (same adjustments as before, with additional formatting)
Here is where it gets really interesting. The 'k' parameter is different from the one in the previous HTTP request: the threat actor has created another Java payload to use in this operation. We used 'jdec' again to decompile it:
Figure 11: Decompiled 'Exec' class payload
The 'Exec' class functionality is very straightforward: get the command provided in the 'i' parameter, execute it, and return the 'stdout' and 'stderr' in the HTTP response (wrapped in the marker string '()').
Noteworthy is the way the command is executed: "/bin/sh -c /usr/bin/env /bin/sh -p -c '<command>'". Remember that the installation of the webshell in '403.jsp' also sets the 'setuid' bit to '/usr/bin/env'? That way, the passed command is executed as 'root'.
As you can see in Figure 10 above, the attacker executed 3 commands like this:
- 'hostname'
This returns the server name to the threat actor, most likely for additional reconnaissance. - 'mysqldump --databases mifs --tables [tables] > /var/www/ext/html/tmp'
This commands dumps 7 tables from the Ivanti 'mifs' database and saves the output to a file 'tmp'. The dumped tables contain credentials and other sensitive data of the mobile devices managed by EPMM. - 'tar -czvf /var/www/ext/html/tmpno /mi/files/system'
This creates an archive 'tmpno' of '/mi/files/system', a directory consisting of a few configuration files containing sensitive data such as admin account credentials for the EPMM installation.
Both files 'tmp' and 'tmpno' were written to a web-accessible directory. The attacker then fetched these files from the server via a simple HTTP request.
These actions also make the attacker's goal very clear: stealing of credentials and other sensitive data. And since the whole attack was most likely fully automated, they were well prepared: they obviously have studied EPMM on beforehand so that they knew exactly where the sensitive data is stored.
Finally, the attacker attempted to cover their tracks and removed the created files from the server by executing an 'rm' command via the same webshell payload.
While this 'Exec' class shows code overlap with a corresponding AntSword payload, it is more heavily customized than the JSP webshell and the 'Info' payload. The methods 'equals()' and 'ExecuteCommandCode()' have been significantly simplified, and redundant methods have been removed.
Fun fact: while the 'Info' class has been compiled as file format version 49 and thus compatible with Java runtime version 5 and newer, the 'Exec' class has file format version 61 and requires at least Java 17. This makes the backwards compatibility code in the webshell rather pointless, it also suggests that both payloads were likely created and compiled by two different threat actors.
Conclusion
This investigated incident is a concrete example of how rapidly attackers can start mass-exploiting certain vulnerabilities as soon as they become publicly known. The reuse and quick customization of open-source offensive frameworks like AntSword enables threat actors to perform fully automated attacks with minimal effort, reducing the time between disclosure and exploitation to a few days or even hours.
While some threat actors rely on opportunistic and trial-and-error exploitation, there are always others that invest additional effort to develop a proper attack chain that will get maximum profit out of an exploitable system. In this case, an attacker successfully exfiltrated all sensitive data from the Ivanti EPMM server, all within just a few seconds, leaving no time for defenders to react manually to try and stop the attack.
The case also underscores the importance of applying security updates as quickly as possible, and robust exposure management for internet-facing systems.
Special thanks to Mikko Poussu from our Incident Response team for his contributions to the investigation.
Indicators of Compromise (IOCs)
| Value | Description |
|---|---|
| 157[.]20[.]182[.]49 | Attacker IP address |
| 62[.]84[.]168[.]208 | Attacker IP address |
| 195[.]216[.]177[.]70 | Attacker IP address |
| 194[.]35[.]226[.]128 | Attacker IP address |
| 185[.]239[.]140[.]40 | Attacker IP address |
| 64[.]226[.]156[.]242 | Attacker IP address |
| 115[.]167[.]65[.]16 | Attacker IP address |
| 46[.]34[.]44[.]66 | Attacker IP address |
| 85[.]17[.]145[.]7 | Attacker IP address |
| 193[.]32[.]249[.]162 | Attacker IP address - performed automatic data exfiltration |
| 46[.]151[.]182[.]30 | Attacker IP address |
| 155[.]212[.]242[.]98 | Attacker IP address |
| 146[.]70[.]41[.]193:443 | Reverse shell IP:Port |
| 23[.]227[.]199[.]80:443 | Reverse shell IP:Port |
| "=theValue%20%20" | URL pattern indicating exploitation |
| "h=gPath%5B%60" | URL pattern indicating exploitation |