- Status Closed
-
Assigned To
cbay - Private
Opened by zlynv - 24.09.2026
Last edited by cbay - 25.09.2026
FS#498 - API accepts unvalidated site path
Summary field (paste as task title): API accepts unvalidated site path ("../") - HTTP docroot escapes account directory, files outside site root served via own domain
—
Details field:
Affected endpoint (in scope) https://api.alwaysdata.com/v1/site/ (update operation) and the resulting vhost on the customer site (e.g. https:<account-site>.alwaysdata.net/)
Summary
The API "update" operation on the site resource accepts an arbitrary path value without validation or normalization. Setting it to a relative traversal ("..") rewrites the web server vhost DocumentRoot so it points outside the site directory (and outside the account directory, resolving to /home). Any file under that escaped root is then served over HTTP through the attacker's own site - i.e. the website can read paths it was never supposed to reach, in the worst case including other customers' files under /home.
The same field rejects nothing: it silently strips leading slashes ("/etc" is stored as "etc/") and accepts ".."" verbatim.
Steps to reproduce
1. Create a test account with a site (path www/).
2. Confirm normal state:
<code>
GET https://api.alwaysdata.com/v1/site/<id>/ → "path": "www/"
GET https:<site>.alwaysdata.net/ → 200 (serves www/)
GET https:<site>.alwaysdata.net/docs/www/index.html → 404 (URL does not exist under legit docroot)
</code>
3. Set a traversal path:
<code>
PATCH https://api.alwaysdata.com/v1/site/<id>/
Authorization: Basic <token-id>:
Content-Type: application/json
{"path":"../"}
</code>
→ 204 No Content (no validation error). GET confirms "path": "../".
4. Prove the docroot escaped (the key control: these URLs are IMPOSSIBLE under the legitimate docroot /home/<account>/www):
<code>
GET https:<site>.alwaysdata.net/ → 403 (docroot now resolves to /home, indexes disabled)
GET https:<site>.alwaysdata.net/docs/www/index.html → 200, body = the site's own index.html
GET https:<site>.alwaysdata.net/docs/admin/ → 403 (account-level directory resolves outside site root)
</code>
The /docs/www/index.html request only returns 200 because DocumentRoot became /home - the file physically lives at /home/<account>/www/index.html and is served via URL path /docs/www/. Under the correct docroot the same URL is 404.
5. Restore:
PATCH {"path":"www/"} -> 204
GET / -> 200 (normal again)
GET /docs/www/index.html -> 404 (escape gone)
Impact
Any customer (or attacker who registers an account) can point their site's document root outside their account by setting path to ".." (or deeper traversals). The web server then serves any filesystem path it can read through the attacker's own hostname.
Potential impact: Because the supplied path can resolve outside the account root, the site's DocumentRoot can potentially encompass /home and other account directories. If those paths are readable by the account's HTTP service/container, this could permit cross-tenant disclosure of other customers' files. I did not access other customers' data, in accordance with the program rules.
Suggested classification: High (customer data), potentially Critical depending on what the web server process can read - your call under worst-case analysis.
I did not attempt to fetch any other account's files (program rules) - only paths inside my own test account were requested, and only to prove the escape.
Root cause
The path field of the site resource is not validated server-side: no rejection of "..", no canonicalization check that the resolved path stays within the account directory before applying it to the web server configuration.
Additional observations (not standalone reports)
- Same field silently normalizes: "/etc" is stored as "etc/" (leading slash stripped, no error).
- Mass assignment on readonly field: PATCH {"id":12345} returns 204 but id is unchanged (ignored - OK).
- Field cross-mutation: PATCH {"annotation":"recon-test"} also set "name" to "recon-test" on the site (unexpected side effect, minor).
- With path "../" the site root returns 403 rather than 404, leaking that the path exists (minor).
Account used for testing
- Same test customer account as my previous WebDAV task (panel login available on request).
- Site restored to path "www/" immediately after verification; site confirmed serving normally (200).
- No files outside my own account were accessed at any point.
Loading...
Available keyboard shortcuts
- Alt + ⇧ Shift + l Login Dialog / Logout
- Alt + ⇧ Shift + a Add new task
- Alt + ⇧ Shift + m My searches
- Alt + ⇧ Shift + t focus taskid search
Tasklist
- o open selected task
- j move cursor down
- k move cursor up
Task Details
- n Next task
- p Previous task
- Alt + ⇧ Shift + e ↵ Enter Edit this task
- Alt + ⇧ Shift + w watch task
- Alt + ⇧ Shift + y Close Task
Task Editing
- Alt + ⇧ Shift + s save task
Hello,
But those paths are not readable.
Anyway, that's not a vulnerability. You can simply do a "ls -l /" over SSH, it's even easier.
Kind regards,
Cyril