Summary
The bundled Caddy reverse-proxy's admin API — which has no authentication by default — is bound on 0.0.0.0:2019 inside the container. While this listener is not directly published to the host by docker-compose.yml, it is reachable from the Appsmith server process itself and, crucially, from the SSRF vulnerability reported in the companion advisory (insufficient host denylist in WebClientUtils). An authenticated low-privileged user can therefore drive the SSRF to issue POST /load (or any other admin-API call) against http://0.0.0.0:2019/, fully replacing the live Caddy configuration and taking over the reverse proxy.
Because the SSRF gives the attacker full control over HTTP method, headers, and body, every endpoint of the Caddy admin API is reachable end-to-end through it — there is no in-container shell, no host access, and no additional credential required. The Caddy admin endpoint being bound to 0.0.0.0 rather than 127.0.0.1 is not itself the entry vector, but it is the dangerous functionality that makes the SSRF terminal: without the admin API listener, the SSRF would only reach passive internal HTTP endpoints; with it, a single authenticated REST API action is sufficient to take over the proxy.
In Kubernetes deployments where the Helm chart's metrics.enabled=true is set, the same listener is additionally exposed cluster-wide (no SSRF required) because Caddy's metrics global directive serves Prometheus on the same admin endpoint. See the Helm variant section below.
Affected code
deploy/docker/fs/opt/appsmith/caddy-reconfigure.mjs (line ~45):
{
admin 0.0.0.0:2019
...
servers {
...
metrics
}
}
deploy/helm/values.yaml:
metrics:
enabled: false # ← when set to true ...
port: 2019 # ← ... the metrics Service publishes the admin port
deploy/helm/templates/service-metrics.yaml exposes targetPort: metrics which resolves to containerPort: 2019 — the Caddy admin endpoint.
Reachability via the SSRF (primary exploitation path)
The admin API exposes POST /load, POST|GET /config/*, POST /stop, etc., all unauthenticated. POST /load accepts a complete replacement config (JSON).
Paired with the vulnerability in GHSA-m23h-pvf3-2m7p, the REST API datasource plugin will dispatch any method and body the attacker chooses against the Caddy admin listener. The attacker fully controls the HTTP request — exactly what is needed to call POST /load with a malicious config.
Impact (post-exploitation via Caddy admin)
Once a malicious Caddy config is loaded, the attacker controls the reverse proxy:
file_server with root / → exfiltrate /appsmith-stacks/configuration/docker.env (which contains APPSMITH_REDIS_PASSWORD, APPSMITH_ENCRYPTION_PASSWORD, APPSMITH_ENCRYPTION_SALT, Mongo/Postgres credentials and the supervisor password).
- Strip auth on
/api/* routes; serve a phishing UI on / to harvest credentials of logged-in admins.
- Reverse-proxy internal-only services.
- Intercept and MITM all traffic to the instance.
The leaked encryption keys and Redis password from docker.env then enable Spring Session hijacking (read spring:session:sessions:* from Redis using the leaked password) and decryption of stored datasource credentials, completing the path to a super-admin account and ultimately host RCE (see companion advisory #3 on /env infrastructure repointing).
Reproduction (Docker, via SSRF)
- Log in to a target Appsmith instance with any account.
- Create a REST API action with:
- URL:
http://0.0.0.0:2019/config
- Method:
GET
- Headers:
Content-Type: application/json
- Run the action — Caddy responds with HTTP 200 and the config is shown.
Reproduction (Kubernetes, direct — no SSRF required)
With metrics.enabled=true, from any pod in the cluster:
curl http://<release>-appsmith-metrics:2019/config/
Suggested fix
- Bind the admin endpoint to
127.0.0.1:2019.
- If remote metrics are required, expose Prometheus metrics on a separate, dedicated listener that does not also serve the admin API. (Caddy supports configuring
metrics independently from admin.)
- In the Helm chart, ensure the metrics Service never targets the admin port; provision a metrics-only listener and publish that.
Workarounds
- Do not set
metrics.enabled=true in the Helm chart until patched.
- Add a NetworkPolicy denying ingress to port 2019.
References
- Source:
deploy/docker/fs/opt/appsmith/caddy-reconfigure.mjs
- Source:
deploy/helm/templates/service-metrics.yaml, deploy/helm/values.yaml
- Caddy admin docs: https://caddyserver.com/docs/api
Credits
Igor Benevides (igor4020)
Summary
The bundled Caddy reverse-proxy's admin API — which has no authentication by default — is bound on
0.0.0.0:2019inside the container. While this listener is not directly published to the host bydocker-compose.yml, it is reachable from the Appsmith server process itself and, crucially, from the SSRF vulnerability reported in the companion advisory (insufficient host denylist inWebClientUtils). An authenticated low-privileged user can therefore drive the SSRF to issuePOST /load(or any other admin-API call) againsthttp://0.0.0.0:2019/, fully replacing the live Caddy configuration and taking over the reverse proxy.Because the SSRF gives the attacker full control over HTTP method, headers, and body, every endpoint of the Caddy admin API is reachable end-to-end through it — there is no in-container shell, no host access, and no additional credential required. The Caddy admin endpoint being bound to
0.0.0.0rather than127.0.0.1is not itself the entry vector, but it is the dangerous functionality that makes the SSRF terminal: without the admin API listener, the SSRF would only reach passive internal HTTP endpoints; with it, a single authenticated REST API action is sufficient to take over the proxy.In Kubernetes deployments where the Helm chart's
metrics.enabled=trueis set, the same listener is additionally exposed cluster-wide (no SSRF required) because Caddy'smetricsglobal directive serves Prometheus on the same admin endpoint. See the Helm variant section below.Affected code
deploy/docker/fs/opt/appsmith/caddy-reconfigure.mjs(line ~45):{ admin 0.0.0.0:2019 ... servers { ... metrics } }deploy/helm/values.yaml:deploy/helm/templates/service-metrics.yamlexposestargetPort: metricswhich resolves tocontainerPort: 2019— the Caddy admin endpoint.Reachability via the SSRF (primary exploitation path)
The admin API exposes
POST /load,POST|GET /config/*,POST /stop, etc., all unauthenticated.POST /loadaccepts a complete replacement config (JSON).Paired with the vulnerability in GHSA-m23h-pvf3-2m7p, the REST API datasource plugin will dispatch any method and body the attacker chooses against the Caddy admin listener. The attacker fully controls the HTTP request — exactly what is needed to call
POST /loadwith a malicious config.Impact (post-exploitation via Caddy admin)
Once a malicious Caddy config is loaded, the attacker controls the reverse proxy:
file_serverwithroot /→ exfiltrate/appsmith-stacks/configuration/docker.env(which containsAPPSMITH_REDIS_PASSWORD,APPSMITH_ENCRYPTION_PASSWORD,APPSMITH_ENCRYPTION_SALT, Mongo/Postgres credentials and the supervisor password)./api/*routes; serve a phishing UI on/to harvest credentials of logged-in admins.The leaked encryption keys and Redis password from
docker.envthen enable Spring Session hijacking (readspring:session:sessions:*from Redis using the leaked password) and decryption of stored datasource credentials, completing the path to a super-admin account and ultimately host RCE (see companion advisory #3 on/envinfrastructure repointing).Reproduction (Docker, via SSRF)
http://0.0.0.0:2019/configGETContent-Type: application/jsonReproduction (Kubernetes, direct — no SSRF required)
With
metrics.enabled=true, from any pod in the cluster:Suggested fix
127.0.0.1:2019.metricsindependently fromadmin.)Workarounds
metrics.enabled=truein the Helm chart until patched.References
deploy/docker/fs/opt/appsmith/caddy-reconfigure.mjsdeploy/helm/templates/service-metrics.yaml,deploy/helm/values.yamlCredits
Igor Benevides (
igor4020)