TL;DR
Trello supports two ways to create an account: email/password signup (requires email verification) and Google OAuth (no verification needed — Google already verified the email). The vulnerability exploits the gap between these two paths.
An attacker creates a Trello account using the victim's email address via the email/password path, but never verifies the email — they just stop at the verification prompt. The account exists in Trello's database, linked to the victim's email, in an unverified state. Later, when the victim signs up using Google OAuth with the same email, Trello links the OAuth login to the existing unverified account instead of creating a new one. Now both the attacker (via password) and the victim (via OAuth) have access to the same account — full account takeover.
Background — How Signup and OAuth Work
Most modern web applications offer multiple ways to create an account. Understanding how each path works is key to understanding why this vulnerability exists.
Path 1: Email/Password Signup
The traditional signup flow:
- User enters an email address and password
- Application creates an account in the database (status: unverified)
- Application sends a verification email with a confirmation link
- User clicks the link in their email
- Account status changes to verified
Path 2: Google OAuth Signup
The "Continue with Google" flow:
- User clicks "Continue with Google"
- User is redirected to Google's login page
- User enters their Google credentials and grants consent
- Google sends the user's verified email back to Trello
- Trello creates (or links to) an account using that email — no separate verification needed
The Edge Case
The problem arises when both paths are used for the same email address. Specifically:
- Someone creates an account via email/password with
alice@gmail.combut never verifies - Later, the real
alice@gmail.comsigns up via Google OAuth
What should Trello do? There are two correct options:
- Option A: Reject the OAuth signup because an unverified account already exists with that email — tell Alice "an account with this email already exists, please verify it or use password login"
- Option B: Delete or disconnect the unverified account and create a fresh one linked to the OAuth identity
What Trello actually did: Option C (vulnerable) — link the OAuth login to the existing unverified account, giving both the original registrant (attacker) and the OAuth user (victim) access to the same account.
The Flaw — When Two Signup Paths Collide
Here's the core logic error, visualized:
Timeline:
─────────────────────────────────────────────────────────────────
T1: Attacker registers with alice@gmail.com via email/password
└── Account created: { email: alice@gmail.com, verified: false }
└── Password set by attacker: hunter2
└── Verification email sent (attacker can't click it — not their email)
└── Attacker stops here. Account exists but is unverified.
T2: Real Alice signs up via Google OAuth
└── Google confirms: "alice@gmail.com is verified"
└── Trello checks: "Does an account with alice@gmail.com exist?"
└── Answer: YES (the unverified one from T1)
└── Trello links OAuth to the EXISTING account
└── Account updated: { email: alice@gmail.com, verified: true, oauth: google }
└── Alice is logged in. She thinks this is her new account.
T3: Attacker logs in with alice@gmail.com + hunter2
└── Trello checks: password matches? YES
└── Attacker is logged into Alice's account
└── Full access to everything Alice has done since T2
Result: BOTH the attacker and Alice have active sessions on the SAME account.
Exploitation — Step by Step
Attacker's Side
The attacker only needs to know (or guess) the victim's email address. This is trivial — email addresses are often publicly available from LinkedIn profiles, company directories, GitHub profiles, conference talks, etc.
1. Go to trello.com
2. Click "Create an account"
3. Enter the victim's email: alice@company.com
4. Set a password the attacker controls: P@ssw0rd123
5. Click "Create account"
6. See the "Verify your email" prompt
7. STOP. Do nothing. Close the browser.
└── The account now exists in Trello's database
└── Linked to alice@company.com
└── Password: P@ssw0rd123
└── Status: unverified
└── The attacker just waits...
That's it for the attacker. Total effort: 30 seconds. Now they wait — hours, days, weeks, months — for the victim to sign up.
Victim's Side
The victim does nothing unusual. They simply decide to try Trello for the first time:
1. Go to trello.com
2. Click "Continue with Google"
3. Log in with their Google account (alice@company.com)
4. Grant Trello access to their Google profile
5. Get redirected to Trello — logged in, account ready to use
└── The victim sees a normal, fresh-looking Trello account
└── They start creating boards, inviting team members, adding data
└── They have no idea the attacker also has access
Attacker's Return
At any point after the victim signs up, the attacker comes back:
1. Go to trello.com
2. Click "Log in"
3. Enter: alice@company.com / P@ssw0rd123
4. Successfully logged in — to the VICTIM'S account
└── Can see all boards, cards, and conversations
└── Can see workspace memberships and team data
└── Can change the password (locking victim out)
└── Can change the email (fully hijacking the account)
└── Can export or delete all data
Video PoC — Full Attack Demo
The entire attack from start to finish — attacker registers with the victim's email, skips verification, victim signs up via Google OAuth, and the attacker logs into the victim's account:
Why It Works — The Verification Gap
The vulnerability exists because of a mismatch in how Trello handles email verification across its two signup paths:
function handleOAuthLogin(oauthEmail) {
// Google has verified this email ✓
const existingAccount = findAccountByEmail(oauthEmail);
if (existingAccount) {
// Account already exists — link OAuth to it
existingAccount.linkOAuth("google");
existingAccount.verified = true; // ← Upgrades unverified to verified
loginUser(existingAccount); // ← Victim is logged in
// ✗ Missing: check if existingAccount was verified
// ✗ Missing: invalidate/reset the existing password
// ✗ Missing: alert the user that an account already existed
// ✗ Missing: delete the unverified account and create fresh
} else {
// No account exists — create new one
createAccount(oauthEmail, oauth: "google", verified: true);
loginUser(newAccount);
}
}
The critical line is the if (existingAccount) branch. When an account already exists with the OAuth email, Trello blindly links the OAuth identity to it — even if that account was never verified. The existing password (set by the attacker) remains valid. The attacker's session isn't invalidated. The account just gets "upgraded" to verified status.
Impact
Severity: P2 (High) — Account Takeover
- Confidentiality: Critical — full access to the victim's account, boards, cards, conversations, attached files, and workspace data
- Integrity: Critical — attacker can modify, delete, or export all data; can change account credentials to lock the victim out permanently
- Availability: High — attacker can lock the victim out by changing password and email
What makes this attack dangerous:
- Zero interaction from victim. The victim doesn't need to click a link, visit a malicious site, or do anything unusual. They just sign up for Trello normally via Google OAuth.
- Scalable. An attacker can pre-register hundreds of email addresses. Every future OAuth signup with a matching email is automatically compromised.
- Invisible. No alerts, no notifications, no indicators that the account was pre-compromised. The victim's OAuth login appears to work perfectly.
- Low skill required. No tools, no Burp Suite, no API knowledge. Just a web browser and the victim's email address.
- High-value targets. Trello is used by teams at large organizations. If an attacker pre-registers the email of a CTO, engineering lead, or project manager before they sign up for Trello, the attacker gains access to potentially sensitive project boards, roadmaps, and internal discussions.
Remediation
The standard fix for this vulnerability class involves one or more of these approaches:
- Don't link OAuth to unverified accounts. When an OAuth login matches an existing account, check the account's verification status. If the account is unverified, treat it as an abandoned/suspicious registration — delete it and create a fresh account linked to the OAuth identity.
- Invalidate credentials on OAuth linking. If you choose to link OAuth to an existing account, invalidate all existing authentication methods (password, active sessions, API tokens) on that account. The attacker's password should stop working the moment the victim links their OAuth.
- Expire unverified accounts. Unverified accounts should have a short TTL (e.g., 24-48 hours). If the email isn't verified within that window, delete the account. This reduces the attack window significantly.
- Alert on OAuth linking. When an OAuth login is linked to an existing account (verified or not), send a notification to the email: "Your Trello account was just linked to Google OAuth. If you didn't do this, contact support." This doesn't prevent the attack but makes it detectable.
Timeline
- 2020-08-31 — Original submission by another researcher ("Account Takeover via Google Oauth misconfiguration")
- 2024-10-31 — Independent rediscovery and submission by me (TechyCodec08)
- 2024-11-05 — Marked as duplicate of the August 2020 submission
- No bounty awarded — duplicate with "Not applicable" status
Takeaways
- Always test signup path collisions. When an application supports multiple signup methods (email, Google OAuth, Apple, GitHub, SSO), systematically test what happens when different paths target the same email. Create an unverified account via path A, then sign up via path B. Does path B inherit path A's credentials? Does it create a separate account? Does it alert anyone? The answer reveals whether the application is vulnerable to pre-ATO.
- Pre-ATO is a checklist item, not a discovery. This vulnerability class is well-documented. It appears on OWASP lists, in bug bounty writeups, and in security research papers. If you're testing a new target, "test pre-ATO via OAuth" should be one of the first things you try — before diving into more complex attack surfaces. It takes 60 seconds to test and can yield a P2.
- Unverified state is dangerous state. Any resource in an "unverified" or "pending" state is a potential attack vector. Can an attacker create unverified resources that later get implicitly trusted when a legitimate user arrives? This pattern extends beyond accounts — think unverified email addresses on existing accounts, pending invitations, unconfirmed role changes.
- OAuth trust is not transitive. Google verified Alice's email. But that doesn't mean Trello should trust everything that was previously associated with that email in its own database. An unverified Trello account created with Alice's email isn't trustworthy just because Google later confirms Alice owns the email. The OAuth verification applies to the OAuth identity, not to the pre-existing unverified state in Trello's database.
- Duplicate or not, understand the pattern. I didn't earn money from this finding. But I learned a vulnerability pattern that I now test on every single target. Pre-ATO via OAuth is still present on many applications — the fundamentals don't change even as platforms evolve. Sometimes the best ROI from a duplicate is the pattern it adds to your permanent checklist.