Ghostfolio CVE-2026-47127
MEDIUMSeverity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N
Network callback requires authenticated session (PR:L, AV:N); subscription state is fully manipulated (I:H) with no confidentiality or availability impact.
Primary rating from Vendor (github).
CVSS VectorVendor: github
Lifecycle Timeline
2DescriptionCVE.org
Ghostfolio is an open source wealth management software. Prior to version 3.4.0, Ghostfolio's Stripe checkout success-URL handler at GET /api/v1/subscription/stripe/callback?checkoutSessionId=<id> retrieves the Stripe Checkout Session by ID and unconditionally grants a Premium subscription to the session's client_reference_id - without ever checking session.payment_status or session.status. There is no separate Stripe webhook endpoint with stripe-signature verification; this callback is the sole code path that creates Stripe-driven subscriptions. Any authenticated user can self-grant a 1-year Premium subscription without ever paying. Version 3.4.0 rejects sessions unless session.payment_status = 'paid' AND session.status = 'complete' (fails closed). Additionally, new unique stripeCheckoutSessionId column → a session can't be redeemed twice (race-safe via DB unique constraint).
AnalysisAI
Ghostfolio's Stripe checkout callback endpoint unconditionally grants a one-year Premium subscription to any authenticated user who supplies a valid Stripe Checkout Session ID, regardless of whether payment was completed. Versions prior to 3.4.0 retrieve the session object by ID and immediately create a subscription using the session's client_reference_id without ever asserting session.payment_status = 'paid' or session.status = 'complete'. Any registered user of an affected Ghostfolio instance can self-grant Premium access at zero cost, with no public exploit identified at time of analysis but trivial exploitability given the complete description of the missing check.
Technical ContextAI
Ghostfolio is a NestJS-based open source wealth management platform that integrates Stripe for Premium subscription billing. The vulnerable code path resides in apps/api/src/app/subscription/subscription.service.ts, where the stripeCallback handler retrieves a Stripe Checkout Session object by its ID and reads session.client_reference_id (the platform's user identifier) to create a subscription record via Prisma ORM. CWE-862 (Missing Authorization) is the root cause: the authorization gate - verifying that the Stripe session reflects a completed, paid transaction - was entirely absent. No separate Stripe webhook endpoint with HMAC stripe-signature verification existed; this callback was the sole code path for Stripe-driven subscription creation. The pre-fix schema also lacked a unique constraint on stripeCheckoutSessionId, making the endpoint vulnerable to race-condition replay. The fix in PR #6872 adds a mandatory dual-condition check (payment_status = 'paid' AND status = 'complete'), wraps subscription creation in a try-catch for Prisma unique-constraint violations (error code P2002), and adds a stripeCheckoutSessionId TEXT UNIQUE column to the Subscription table via a new Prisma migration.
Affected ProductsAI
Ghostfolio versions prior to 3.4.0 are affected, specifically deployments with Stripe subscription integration enabled. The vulnerability is in the subscription service module at apps/api/src/app/subscription/subscription.service.ts. No CPE string was provided in the input data. The authoritative vendor security advisory is published at https://github.com/ghostfolio/ghostfolio/security/advisories/GHSA-j465-x2w3-wjj8 and the fix is documented in GitHub PR #6872 at https://github.com/ghostfolio/ghostfolio/pull/6872.
RemediationAI
Upgrade to Ghostfolio version 3.4.0, which is confirmed by the vendor advisory at https://github.com/ghostfolio/ghostfolio/security/advisories/GHSA-j465-x2w3-wjj8 and implemented in PR #6872 (https://github.com/ghostfolio/ghostfolio/pull/6872). The fix enforces dual payment verification (session.payment_status = 'paid' AND session.status = 'complete') and applies a database-level unique constraint on stripeCheckoutSessionId to prevent replay. If immediate upgrade is not feasible, operators can block access to GET /api/v1/subscription/stripe/callback at the reverse proxy or firewall layer - note this will disable all Stripe-driven subscription creation until the patch is applied, which is acceptable only if Premium subscription sales can be temporarily suspended. Operators should also audit the Subscription table for records with no corresponding Stripe payment (price near zero or stripeCheckoutSessionId null on recently created rows) to identify accounts that may have already exploited this flaw. No other workaround preserves both security and subscription functionality simultaneously.
Same weakness CWE-862 – Missing Authorization
View allSame technique Authentication Bypass
View allShare
External POC / Exploit Code
Leaving vuln.today