Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
ZeroDays CVE-2026-63085
High CVE-2026-63085 CVSS 8.8 Axelor Open Platform Java

Axelor Privilege Escalation via Nested Save

CVE-2026-63085 is a high-severity authorization bypass in Axelor Open Platform 8.x that lets authenticated users escalate to admin by abusing nested JPA save paths.

Offensive360 Research Team
Affects: 8.0.0 - 8.2.1
Source Code View Patch

Overview

CVE-2026-63085 is an authorization bypass vulnerability affecting Axelor Open Platform versions 8.x prior to 8.2.2. The flaw allows any authenticated, non-administrative user to escalate their own privileges — including granting themselves administrative roles and group memberships — without ever directly invoking a privileged API endpoint. The vulnerability carries a CVSS score of 8.8 (High), reflecting the low attack complexity and significant integrity impact against a multi-tenant enterprise application platform.

The root cause lies in how Axelor’s JPA-backed persistence layer handles nested relational save operations. When a user saves changes to a related entity that holds a reference back to a User record, the platform’s USER_RESTRICTED_FIELDS enforcement mechanism is not applied to the indirectly modified User object. This means an attacker can embed role and group assignments inside a nested payload, and the persistence context will flush those changes to the database on transaction commit — completely bypassing the intended field-level access control.

Axelor Open Platform is a widely deployed Java-based low-code ERP and business application framework used across finance, HR, and operations management in enterprise environments. Any organization running a version in the 8.x series prior to 8.2.2 that allows general employee or partner portal access should treat this as an immediately exploitable privilege escalation path.

Technical Analysis

Axelor implements a concept of restricted fields on the User entity to prevent non-admin users from modifying sensitive attributes such as roles, group, and permissions. In the normal direct-save code path, the service layer checks incoming field names against USER_RESTRICTED_FIELDS and strips any unauthorized modifications before persisting.

The problem is that this guard is only applied at the top-level entity during a save request. When the platform processes a save for a related entity — say, an Employee or Contact record that holds a user association — the nested User object within that payload bypasses the restricted-field check entirely. Axelor’s generic REST save handler deserializes the full nested graph and merges it into the current JPA persistence context. When the transaction commits, Hibernate flushes all dirty entities in the session, including the attacker-modified User object.

A simplified representation of the vulnerable pattern looks like this:

// VULNERABLE: Axelor's generic save handler (simplified)
public Response save(String modelName, Request request) {
    Model entity = deserializeAndMerge(modelName, request.getData());

    // Restricted field check is only applied when the top-level
    // model is "User". Nested User objects within related entities
    // are merged directly into the JPA context without this guard.
    if (entity instanceof User) {
        stripRestrictedFields((User) entity, USER_RESTRICTED_FIELDS);
    }

    // JPA flush — all dirty entities in the session are persisted,
    // including any nested User record modified by the attacker.
    JPA.manage(entity);
    JPA.flush();

    return Response.ok(entity);
}

private Model deserializeAndMerge(String modelName, Map<String, Object> data) {
    Model entity = JPA.find(modelName, (Long) data.get("id"));
    // Recursively merges nested objects into the persistence context
    // without applying per-entity field restrictions.
    mergeNestedRelations(entity, data);
    return entity;
}

An attacker crafts a JSON payload targeting a save endpoint for a related entity they have legitimate write access to — for example, POST /ws/rest/com.axelor.auth.db.User/{id}/related-entity/save. Inside the user nested object within that payload, they inject roles and group fields referencing admin role IDs they have enumerated:

{
  "data": {
    "id": 42,
    "name": "My Profile",
    "user": {
      "id": 7,
      "roles": [{ "id": 1 }],
      "group": { "id": 1 }
    }
  }
}

Because mergeNestedRelations recursively walks the object graph and loads these role and group entities by ID into the managed User instance, the JPA dirty-checking mechanism detects the collection change and includes it in the flush. The stripRestrictedFields guard is never invoked on this code path.

Impact

A successful exploit grants an attacker full administrative access to the Axelor platform. From that position, they can read and exfiltrate all business data across all modules — including HR records, financial ledgers, customer data, and operational workflows. They can create backdoor admin accounts, disable audit logging, modify access control configurations, and deploy server-side scripts through Axelor’s Groovy-based action engine, potentially achieving remote code execution within the application context.

In terms of CVSS vectors (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H), the attack requires only a valid user session, is executable over the network with no special conditions, and requires no user interaction. The integrity and confidentiality impact is complete against the application’s data. Organizations running Axelor as their core ERP system face potential full business data compromise from a single compromised employee account.

How to Fix It

The immediate remediation is to upgrade to Axelor Open Platform 8.2.2, which enforces USER_RESTRICTED_FIELDS checks recursively across the entire deserialized entity graph, not just at the top-level entity type.

For teams managing dependencies via Maven:

<dependency>
    <groupId>com.axelor</groupId>
    <artifactId>axelor-core</artifactId>
    <version>8.2.2</version>
</dependency>

The corrected pattern applies the restricted-field guard during the recursive merge phase, not just at the top level:

// FIXED: Apply restricted field checks during nested merge
private void mergeNestedRelations(Model entity, Map<String, Object> data) {
    for (Map.Entry<String, Object> entry : data.entrySet()) {
        String field = entry.getKey();
        Object value = entry.getValue();

        if (value instanceof Map) {
            Model nested = resolveNestedEntity(field, (Map<String, Object>) value);

            // Apply restricted field enforcement regardless of call depth
            if (nested instanceof User) {
                stripRestrictedFields((User) nested, USER_RESTRICTED_FIELDS);
            }

            setField(entity, field, nested);
        }
    }
}

Beyond upgrading, teams should audit any custom Axelor modules that implement their own save handlers to ensure they apply the same recursive field restriction logic. Network-level controls — such as restricting the /ws/rest/ endpoints to authenticated internal networks only — reduce the exposure window but do not eliminate the vulnerability and should not substitute for patching.

Our Take

This vulnerability is a textbook example of an enforcement boundary mismatch: developers correctly identified sensitive fields and wrote a guard, but anchored that guard to a single code path rather than to the data model itself. As object graphs grow deeper and REST APIs become more generic, these “ghost lanes” around security controls multiply. We see this pattern repeatedly across enterprise Java platforms — the JPA session as an implicit write buffer is particularly dangerous because its flush behavior is not always intuitive to developers focused on business logic.

For enterprises, the lesson is that access control must be enforced as close to the persistence layer as possible, not only at the controller or service boundary. Field-level security that lives exclusively in a pre-save hook will always be vulnerable to any alternate path into the JPA session. Architecturally, treating User role and group assignments as append-only operations requiring a dedicated, tightly scoped service call — rather than allowing them as inline nested updates — would eliminate this entire class of bypass.

Detection with SAST

This vulnerability class maps to CWE-863 (Incorrect Authorization) and CWE-284 (Improper Access Control). SAST detection focuses on identifying divergent enforcement paths in recursive or generic persistence handlers.

Offensive360’s analysis engine flags the following patterns in Java JPA codebases:

  • Recursive entity merge functions where type-based authorization checks (instanceof guards) are only present at the entry point and not within the recursive descent
  • JPA merge() or flush() calls that operate on object graphs deserialized from external input without a full-graph field restriction pass
  • Generic REST handlers that accept arbitrary nested JSON and map it to managed entities, particularly when the mapping logic is separated from the authorization logic by more than one call frame
  • Conditional field stripping tied to entity type at a single stack depth, rather than applied as a cross-cutting concern via entity lifecycle callbacks (@PrePersist, @PreUpdate) or JPA entity listeners

Encoding these checks as dataflow rules — tracing attacker-controlled JSON input from deserialization through JPA.manage() or EntityManager.merge() to flush(), while verifying that a field restriction gate dominates all paths — reliably surfaces this vulnerability pattern before it reaches production.

References

#authorization-bypass #privilege-escalation #JPA #broken-access-control

Detect this vulnerability class in your codebase

Offensive360 SAST scans your source code for CVE-2026-63085-class vulnerabilities and thousands of other patterns — across 60+ languages.