On October 9, 2026, I reported an unauthenticated server side request forgery in the Euclid Data Space Portal operated by the European Space Agency. The portal exposes a REST API for managing scientific data collections. One of its endpoints, POST /api/v1/collections/{acronym}/overview/link, accepts a URL in the request body, fetches it from the server, stores the response, and returns the full response body to the caller. The endpoint required no authentication. An anonymous attacker could make the server issue HTTP requests to any destination of their choosing and read the complete response.
What I found
The route and its request schema were visible in the unauthenticated OpenAPI document served at /api/openapi.json. The endpoint accepted a JSON body with a single field, link, containing the URL the server would fetch. Sending a POST with {"link":"https://example.com/"} returned a 200 response whose overview_content field contained the full HTML of example.com, title element and all. The server fetched the URL and returned its body inline.
To confirm the fetch originated server side and not from the client, I pointed the link at a unique collector endpoint under my control. The collector recorded an inbound GET request from an IP belonging to the ESA portal infrastructure, and the server returned the collector's response body as overview_content.
Internal network reach
The fetch applied no egress restriction. The only destination the server rejected was the file scheme, returning "Invalid URL format". Every other destination was actually attempted, and the server reported the real network result, whether that was a successful response, a connection timeout, or a TLS error.
The internal application backend was readable in full. A request to http://127.0.0.1:8000/api/v1/health/ready returned the JSON health check response:
{"status":"healthy","subsystems":{"database":{"status":"healthy",
"details":"Database connection pool is operational"}}}
Port 8000 on loopback is the application backend sitting behind the public web server. A request to the root of that port returned a 404 from a different handler than the public edge, confirming the response came from the internal service.
The Kubernetes control plane was also reachable. A request to https://kubernetes.default.svc/version returned an SSL error citing a self-signed certificate in the certificate chain. That is the cluster's own certificate authority. The fetch resolved the in-cluster service name kubernetes.default.svc, which is not resolvable from the internet, and completed a TLS handshake with the Kubernetes API server over the pod network. The read did not complete only because the fetcher verified certificates, which is a configuration detail rather than a security boundary.
What the negatives revealed
The cloud instance metadata endpoint at http://169.254.169.254/ timed out, and the internal name for the pre-production environment did not resolve from the pod. Together these confirmed the platform is an on-premises Kubernetes deployment rather than a public cloud, so no instance metadata service was available to extract credentials from. The exposure was internal network access, not cloud credential theft.
Root cause
The overview link route fetched the caller-supplied URL with no authentication requirement and no restriction on the destination. There was no allowlist of schemes or hosts, no block on private, loopback, or link-local address ranges, no restriction on redirects, and no entitlement check on who could write to the collection's overview. The full response body was both stored and returned to the caller.
Impact
Unauthenticated, unfiltered, full-read server side request forgery from an internet-facing ESA data portal into the internal Kubernetes network behind it. With no account, an attacker could enumerate and read internal services on the pod network and on loopback. This was proven by reading the internal backend health endpoint and by reaching the Kubernetes API server. The path leads to internal reconnaissance and to reading services, databases, and management endpoints that are not meant to be reachable from the internet.
The fix
ESA removed the vulnerable endpoint entirely. On retest, the POST to /api/v1/collections/{acronym}/overview/link returns 404 Not Found across all collections. The route that performed the server-side fetch is gone. The unauthenticated DELETE on /overview now returns 401, which closes the secondary unauthenticated write that allowed restoring records without credentials.
Timeline
09 Oct 2026 Submitted through ESA VDP on Intigriti
12 Oct 2026 Triaged, severity set to High (7.5), forwarded to ESA
13 Oct 2026 ESA acknowledged internal remediation underway
14 Oct 2026 ESA confirmed fix deployed, retest requested
14 Oct 2026 Retest confirmed: endpoint removed (404), DELETE auth-gated (401)
14 Oct 2026 Accepted and resolved
CWE-918 (Server-Side Request Forgery). Reported through the ESA Vulnerability Disclosure Program on Intigriti.
A server that fetches a caller-supplied URL and returns the body has made the internet's trust boundary its own, and its own internal network is now on the wrong side of it.