
Lago GraphQLのacceptInvite認証バイパスを実証するPoCラボ。未認証の招待承諾を通じてクロス組織のアカウント乗っ取りを可能にする重大な脆弱性を示す。
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · lago-invite-ato
Lago v1.53.0 - GetLago
Mutations::Invites::Accept には AuthenticableApiUser がありません。Create/revoke にはあります。Accept にはありません。register_from_invite は招待メールでユーザーを読み込みます。そのアカウントがすでにメンバーシップを持っている場合、送信されたパスワードは破棄されます。それでも AuthToken.encode(user:) は実行されます。JWT は ユーザースコープ です。org クレームはありません。クライアントは、そのユーザーがすでに所属している任意の org に対して x-lago-organization を送信します。被害者の org も含まれます。createInvite はすでに招待者にトークンを返します。
cfo@victim を使い捨ての org に招待し、自分で承諾すれば、そのメールがすでに存在するすべての org でその人物になれます。メールボックスは一切関与しません。被害者のパスワードは変更されません。
| ID | CVE 未採番 |
| CWE | CWE-287, CWE-306 |
| CVSS | Critical: 9.6 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N |
| Product | Lago / lago-api |
| Affected | v1.53.0 までの email/password acceptInvite |
| Auth | org admin (organization:members:create); Cloud のサインアップで十分 |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 のみ |
自分が管理する org が必要です。Cloud のサインアップで十分です(LAGO_DISABLE_SIGNUP のデフォルトは false)。被害者のメールを招待し、ダミーパスワードで未認証のまま承諾し、その後ヘッダーを Victim Corp に切り替えます。請求書、API キー、Stripe、ウォレット。
アカウントを持たないランダムな訪問者ではありません。パスワードリセットでもありません。ダミーログインは失敗します。本物の被害者パスワードは依然として機能します。Google/Okta の accept は IdP のメールをバインドするため、このバグではありません。DB に他人のメールが存在しない単一 org のセルフホストでは、別のマーチャントにはなれません。同じデータベース、2 つの org、同じメール:それは可能です。
Stripe ワンタイム請求書 IDOR と同じツリーです。別のバグです。
Accept(AuthenticableApiUser なし)を読み、次に find_or_initialize_by(email:)、次にパスワードをスキップするメンバーシップ存在分岐、次に AuthToken.encode(user:) を読みました。次に Google accept ですが、これは google_oidc["email"] == invite.email を チェックします。email/password accept は既存ユーザーのパスワードをバインドしません。
これを最初に調べるクライアントは、未使用のアドレスを招待し、それを承諾し、送信したパスワードで真新しい Lago ユーザーを取得します。それが機能です。証拠となるのは、既存の 被害者ユーザー id が返ってくることです。
すでに記録済みの誤った道:Google/Okta で承諾すること。JWT なしの createInvite 401 をすべてと見なすこと(create は認証済み、accept は未認証)。currentUser で攻撃者の org だけを見ること(手がかりはリスト内の Victim Corp)。ダミーパスワードでログインできること(それはハッシュが上書きされたことを意味する - ラボはそうでないことを検証済み)。リバースシェル。芝居。受信トレイなし。
そして:2 回の registerUser 呼び出しで Victim Corp と Attacker Corp を仕込みます。毎回新しいメールを使い、汚れたボリュームで user_already_exists の 422 にならないようにします。被害者のメールで createInvite。ダミーパスワードで未認証の acceptInvite。返されたユーザー id が被害者と一致。currentUser.organizations に両方がリストされる。ヘッダー切り替えで Victim Corp が返る。
cd lab
./run.sh
対象は http://127.0.0.1:13000 のみ。Stripe IDOR は 13001 を使用。UI ポートはなし。
IOC victim_password_unchanged
IOC victim_real_password_still_works
SUCCESS LAGO-INVITE-ATO acceptInvite JWT is existing victim; lists Victim Corp; no inbox
ユーザーがすでに存在する場合、メンバーシップを付与する前に本人として認証する(またはメールボックス / IdP を証明する)ことを要求します。未認証の accept から既存ユーザー用の JWT を発行しないでください。招待 token がケイパビリティである場合、招待者に返すのをやめてください。
accept.rb · accept_service.rb · users_service.rb · create.rb · object.rb · google_service.rbGNU Affero GPL v3.0。LICENSE を参照。ループバックラボのみ。無保証。