WPMgr v0.61.199, planted password survives OIDC link
WPMgr v0.61.199 POST /auth/register plants a pending user with an attacker-chosen password. MarkUserEmailVerified does not clear password_hash. A later Google/GitHub/OIDC callback binds the victim identity onto that row. The planted password still logs in. Labbed. No CVE yet.
- Name
- WPMgr v0.61.199, planted password survives OIDC link
- Type
- 0-day analysis
- CVE
- n/a
- CVE Risk
- high
- Disclosure Status
- public
- Vendor
- mosamlife
- Affected
- WPMgr v0.61.199 (e1b3a163). Self-serve after the install is claimed. Victim verifies email, then signs in with SSO.
- Published
- 09 Nov 2026
- Updated
- 09 Nov 2026
- Tags
- 0-day, wpmgr, oauth, account-linking, cwe-287, cwe-284, remote
Register first. Wait for Google.
I planted an account with someone else's email and a password they never chose. WPMgr marked it pending, which is correct, and mailed a verify link, which is also correct. The victim clicked that mail. Then they did the thing a reasonable person does with a fleet manager: they signed in with OIDC. WPMgr linked Google-or-Dex onto the planted row and left my hash sitting there. I logged in with the password I picked. They still had 2FA off. I had a session as them.
Someone who knows your email can do that after the install is claimed. They need you to verify the address. They do not need a mailbox if you click the unexpected verify mail yourself. SSO-only accounts that never had a password are a different story.
This is WPMgr v0.61.199 (e1b3a163988c9f1c31bc93922ea34322fa188792). No CVE yet. CWE-287 / CWE-284. 8.1 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N). Remote OAuth leftover. Pack: abraxas/wpmgr-social-planted-password.
The comment that almost closed it
decideSocial already names this pre-hijack. It refuses to link a provider-verified identity onto a row until email_verified_at is set. That gate is real. The pending path returns email_not_verified and never the copy that would tell the user to reset a password they did not set. ResendVerification is unauthenticated. MarkUserEmailVerified sets status=active and email_verified_at=now() and does not touch password_hash. The next callback hits socialLinkExisting → CompleteSocialLink.
So the recovery the product walks you through is: click the mail, try SSO again. That is exactly the plant.
Pin and skip list
Same pin as the Dex leftover, e1b3a163. MCP OAuth is in-class on paper. Open client registration is bounded to a row. PKCE S256 is mandatory. Redirects are exact-match. Consent sits behind PermAPIKeyManage. I left it closed. SAML is not implemented.
I wasted a lab evening treating an SSO-only empty password_hash as this bug. It is not. There is nothing to keep.
The lab
Compose wpmgrh04, loopback 18180-18186, Mailpit on a 1.2.3.0/24 gadget subnet because the product SMTP dialer (code.dny.dev/ssrf) refuses RFC1918 and loopback. Dex stands in for Google. Bootstrap claim. Plant victim.h04@lab.test as pending|hash|unverified. Mailpit hands over the raw token. Verify leaves active|hash|verified. OIDC links oidc|victim.h04@lab.test. Attacker password login still issues wpmgr_session.
IOC planted-pending pending|hash|unverified
IOC email-verified-hash-kept active|hash|verified
IOC oidc-linked oidc|victim.h04@lab.test
IOC password-login-survived active|hash|verified oidc|victim.h04@lab.test
SUCCESS WPMGR-H04-SOCIAL-PLANT planted-password-survives-oidc-link WPMGR-H04-PLANTED-PASSWORD-SURVIVESWhat an attacker can do
Needs an already-claimed install (self-serve is open) and the victim's email. The victim verifies. Then the victim SSOs. In ordinary words: they register as you, you confirm the mail, you sign in with Google, they still know a password for your account.
| Privilege | Method / sink | Recon / integrity / availability | Lab-proven or inferred |
|---|---|---|---|
| Plant a pending user with an attacker-chosen argon2 password | POST /auth/register |
Integrity (account plant) | Lab-proven |
| Mail a verification token for that planted row | unauthenticated resend / register mail | Recon | Lab-proven. Mailpit |
| Activate the planted row without clearing the hash | POST /auth/verify-email → MarkUserEmailVerified |
Integrity | Lab-proven |
| Bind OIDC onto that same row | GET /auth/oidc/callback → CompleteSocialLink |
Integrity (identity link) | Lab-proven |
| Sign in as the victim with the planted password after SSO | POST /auth/login |
Integrity (session as victim) | Lab-proven |
Stays closed on this leftover
| Axis | Method / sink | Why it is closed |
|---|---|---|
| Integrity | SSO-only empty password_hash |
No planted hash |
| Integrity | MCP OAuth | PKCE, exact redirect, consent grant |
| Integrity | Unclaimed first-run | Bootstrap claim |
| Integrity | Password reset as a session mint | Reset does not issue wpmgr_session |
The fix
On verify, and again in CompleteSocialLink, clear password_hash (or force a reset) when the row was never used for a password login the owner chose. Do not link a provider onto a hash the current user did not set. Show the reset copy on the pending path.
I am @abraxas_null. Site abraxaslabs.tech. GitHub abraxas. Mail abraxas.null@proton.me.