Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
lago-invite-ato — Proof-of-concept lab demonstrating a critical Lago GraphQL acceptInvite authentication bypass that enables cross-organization account takeover via unauthenticated invite acceptance. | Kitploit
Tools/GitHubGitHub/abraxas/lago-invite-ato
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingAuthenticationAPI SecurityLabs & Practice
GitHubabraxas/lago-invite-ato

lago-invite-ato

Proof-of-concept lab demonstrating a critical Lago GraphQL acceptInvite authentication bypass that enables cross-organization account takeover via unauthenticated invite acceptance.

1115 days agoNot yet reviewed
View Repository

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - lago-invite-ato

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  lago-invite-ato

lago-invite-ato

Lago v1.53.0 - GetLago

Mutations::Invites::Accept has no AuthenticableApiUser. Create/revoke do. Accept does not. register_from_invite loads the user by invite email. If that account already has memberships, the posted password is discarded. AuthToken.encode(user:) still runs. The JWT is user-scoped. No org claim. The client sends x-lago-organization for whichever org that user already belongs to. Including the victim's. createInvite already returns the token to the inviter.

Invite cfo@victim into a throwaway org, accept it yourself, and you are that person on every org that email already sits in. Mailbox never involved. Victim password unchanged.

IDno CVE yet
CWECWE-287, CWE-306
CVSSCritical: 9.6 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
ProductLago / lago-api
Affectedthrough v1.53.0 email/password acceptInvite
Authorg admin (organization:members:create); Cloud signup is enough
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

You need an org you control. Cloud signup is enough (LAGO_DISABLE_SIGNUP defaults false). Invite a victim email, accept unauthenticated with a dummy password, then header-switch to Victim Corp. Invoices, API keys, Stripe, wallets.

Not a random visitor with no account. Not a password reset. Dummy login fails. Real victim password still works. Google/Okta accept binds IdP email and is not this bug. Single-org self-host with nobody else's email in the DB does not give you another merchant. Same database, two orgs, same email: it does.

Same tree as the Stripe one-time invoice IDOR. Different bug.

How I found it

I read Accept (no AuthenticableApiUser), then find_or_initialize_by(email:), then the memberships-present branch that skips the password, then AuthToken.encode(user:). Then Google accept, which does check google_oidc["email"] == invite.email. Email/password accept does not bind the password of an existing user.

The first client that looks at this will invite an unused address, accept it, and get a brand-new Lago user with the password they posted. That is the feature. The witness is the existing victim user id coming back.

Wrong turns already recorded: accepting with Google/Okta; treating createInvite 401 without a JWT as the whole story (create is authenticated, accept is not); looking only at the attacker org on currentUser (the tell is Victim Corp in the list); dummy password logging in (that would mean the hash was overwritten - lab asserts it was not); a reverse shell. Theatre. No inbox.

Then: two registerUser calls plant Victim Corp and Attacker Corp. Fresh emails each run so a dirty volume does not 422 user_already_exists. createInvite on the victim email. Unauthenticated acceptInvite with a dummy password. Returned user id matches the victim. currentUser.organizations lists both. Header switch returns Victim Corp.

Lab

cd lab
./run.sh

Target only http://127.0.0.1:13000. Stripe IDOR used 13001. No UI port.

IOC victim_password_unchanged
IOC victim_real_password_still_works
SUCCESS LAGO-INVITE-ATO acceptInvite JWT is existing victim; lists Victim Corp; no inbox

The fix

If the user already exists, require they authenticate as themselves (or prove mailbox / IdP) before attaching the membership. Do not mint a JWT for an existing user from unauthenticated accept. Stop returning invite token to the inviter if it is a capability.

References

  • github.com/getlago/lago tag v1.53.0 · lago-api
  • accept.rb · accept_service.rb · users_service.rb · create.rb · object.rb · google_service.rb
  • Same product: lago-stripe-invoice-idor
  • CWE-287 · CWE-306

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool