fix(security): block OAuth account takeover via pending exchange - #5345
Merged
Wei-Shaw merged 1 commit intoAug 7, 2026
Merged
Conversation
Contributor
|
Thank you for your contribution! Before we can merge this PR, we need you to sign our Contributor License Agreement (CLA). To sign, please reply with the following comment:
You only need to sign once — it will be valid for all your future contributions to this project. I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
Contributor
Author
|
I have read the CLA Document and I hereby sign the CLA
github-actions[bot] ***@***.***> 于2026年8月7日周五 03:41写道:
… *github-actions[bot]* left a comment (Wei-Shaw/sub2api#5345)
<#5345 (comment)>
Thank you for your contribution! Before we can merge this PR, we need you
to sign our Contributor License Agreement (CLA)
<https://github.com/Wei-Shaw/sub2api/blob/main/CLA.md>.
*To sign*, please reply with the following comment:
I have read the CLA Document and I hereby sign the CLA
You only need to sign once — it will be valid for all your future
contributions to this project.
------------------------------
I have read the CLA Document and I hereby sign the CLA
------------------------------
You can retrigger this bot by commenting *recheck* in this Pull Request. Posted
by the *CLA Assistant Lite bot*.
—
Reply to this email directly, view it on GitHub
<#5345?email_source=notifications&email_token=AAVFRBSWHPHHZV32LKDJUFD5ITNPJA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMRQHA3DIMJXHE42M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-5208641799>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAVFRBQSPOOUZ7KZZJ2STIL5ITNPJAVCNFSNUABGKJSXA33TNF2G64TZHMYTCMJYGYYDCNJRHA5US43TOVSTWNJQHA2DCMRSHA2TRILWAI>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/AAVFRBQQ5EOOXIW36FJRXRL5ITNPJA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMRQHA3DIMJXHE42M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KUZTPN52GK4S7NFXXG>
and Android
<https://github.com/notifications/mobile/android/AAVFRBXENAUW2TCEOVJ3Z7D5ITNPJA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMRQHA3DIMJXHE42M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2K4ZTPN52GK4S7MFXGI4TPNFSA>.
Download it today!
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Contributor
Author
|
recheck |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a critical account-takeover vulnerability (0day) in the pending OAuth flow: an attacker who only knows a victim's email address can bind their own LinuxDo / OIDC / WeChat / DingTalk identity to the victim's account and log in as them.
Attack chain
intent=login) and lands on the email-completion step.POST /auth/oauth/pending/create-accountwith an arbitrary password. Because the email already exists, the handler transitions the pending session into the choice state withTargetUserID = victim,existing_account_bindable = true— no password, no email verification code, no captcha, no proof of ownership is ever checked on this path (the captcha check is skipped when the email exists).POST /auth/oauth/pending/exchangewith an adoption decision (adopt_display_name/adopt_avatar— pure booleans unrelated to account ownership).ExchangePendingOAuthCompletiononly guarded againstemail_completion/bind_login_requiredsteps, so the request falls through toapplyPendingOAuthAdoption→applyPendingOAuthBinding, andshouldBindPendingOAuthIdentityreturnstrueunconditionally forintent=login.findOAuthIdentityUserresolves the attacker to the victim and the exchange endpoint issues the victim's token pair.Fix
ExchangePendingOAuthCompletionnow only performs adoption/binding for:canIssueTokenPair == true) — the identity was already safely bound to that user during a completed login;bind_current_usersessions — initiated by an authenticated user whose binding target comes from the login-session cookie.Every other state (including
choose_account_action_required) returns the payload without binding the identity and without consuming the session, so the attacker cannot proceed to the next step either.Testing
TestExchangePendingOAuthCompletionChoiceStateDoesNotBindIdentityreproduces the exact attack (choice-state session + adoption decision → identity not bound, session not consumed, no token issued).TestExchangePendingOAuthCompletion*,TestCreateOIDCOAuthAccount*,TestSendPendingOAuthVerifyCode*, and the fullinternal/handlerpackage tests pass.Recommendation for follow-up (not included)
Defense in depth: when
create-account/send-verify-codediscovers an existing email, the session should transition to a password/2FA-required state (bind_login_requiredsemantics) instead of a choice state that implies bindability. Please also audit existingauth_identitiesrows for bindings whose upstream email does not match the owning user.