google / google/nomulus

SSRF in CannedScriptExecutionAction (Admin-Only)

Open
#3,011 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
1.8k
Forks
302
Avg merge
2d 11h
Merged PRs (30d)
16

Description

**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://:8000 Authorization: Bearer

**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

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.