Skip to content

fix(kubernetes-client): raw query values allow Kubernetes API option smuggling - #7946

Open
GrosQuildu wants to merge 2 commits into
fabric8io:mainfrom
GrosQuildu:ptp-51-query-option-smuggling
Open

fix(kubernetes-client): raw query values allow Kubernetes API option smuggling#7946
GrosQuildu wants to merge 2 commits into
fabric8io:mainfrom
GrosQuildu:ptp-51-query-option-smuggling

Conversation

@GrosQuildu

@GrosQuildu GrosQuildu commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Description

Several Fabric8 Kubernetes Client request builders splice caller-controlled values directly into URL query strings. Values that contain & or = are then parsed by the Kubernetes or OpenShift API server as additional independent query parameters instead of as data inside one parameter value.

The vulnerable call sites are:

  • OperationSupport.getResourceURLForPatchOperation, where fieldManager can inject patch options such as force, dryRun, or fieldValidation.
  • PodOperationContext.getLogParameters, where containerId can inject pod-log options. This helper is reached directly through PodOperationsImpl and through controller log wrappers: JobOperationsImpl, RollableScalableResourceOperation subclasses for Deployment, ReplicaSet, StatefulSet, ReplicationController, the legacy extensions variants, and OpenShift DeploymentConfigOperationsImpl.
  • BuildConfigOperationsImpl.getQueryParameters, where OpenShift binary-build metadata fields and asFile can inject instantiatebinary options.

Exploit Scenario

An internal log service fetches Kubernetes Job logs with a privileged Fabric8 client. The service lets a lower-trusted user choose only the container name:

client.batch().v1().jobs().inNamespace(ns).withName(job)
    .inContainer(userContainer) // <- user input
    .getLog();

The attacker supplies:

c1&previous=true&tailLines=100000

Fabric8 kubernetes-client sends:

/api/v1/namespaces/ns1/pods/job1-hk9nf/log?pretty=false&container=c1&previous=true&tailLines=100000

The API server receives previous and tailLines as real log options. The wrapper intended to expose only container selection, but the caller changes log history and volume controls.

This Java PoC can be added to JobTest. It calls the Fabric8 mock-server client path and fails on the vulnerable implementation because the request is not encoded as one container value:

@Test
@DisplayName("Should encode container while getting logs for a multi-container job")
void testJobGetLogEncodesContainer() {
  Pod jobPod = createJobPod();

  server.expect().get().withPath("/apis/batch/v1/namespaces/ns1/jobs/job1")
      .andReturn(HttpURLConnection.HTTP_OK, createJobBuilder().build())
      .always();

  server.expect().get()
      .withPath("/api/v1/namespaces/ns1/pods?labelSelector=controller-uid%3D3Dc4c8746c-94fd-47a7-ac01-11047c0323b4")
      .andReturn(HttpURLConnection.HTTP_OK, new PodListBuilder().withItems(jobPod).build())
      .once();
  server.expect().get()
      .withPath("/api/v1/namespaces/ns1/pods/job1-hk9nf/log?pretty=false"
          + "&container=c1%26previous%3Dtrue%26tailLines%3D100000")
      .andReturn(HttpURLConnection.HTTP_OK, "hello")
      .once();

  String log = client.batch().v1().jobs().inNamespace("ns1").withName("job1")
      .inContainer("c1&previous=true&tailLines=100000")
      .getLog();

  assertNotNull(log);
  assertEquals("hello", log);
}

Unpatched execution evidence:

MockWebServer received request:
GET /api/v1/namespaces/ns1/pods/job1-hk9nf/log?pretty=false&container=c1&previous=true&tailLines=100000

JobTest.testJobGetLogEncodesContainer » KubernetesClient
Failure executing: GET at:
.../pods/job1-hk9nf/log?pretty=false&container=c1&previous=true&tailLines=100000

Patched verification:

JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -B -ntp -pl kubernetes-client -am \
  -Dsurefire.failIfNoSpecifiedTests=false -Dtest=PodOperationContextTest,PatchTest test

Tests run: 10, Failures: 0, Errors: 0, Skipped: 0

JAVA_HOME=$(/usr/libexec/java_home -v 21) mvn -B -ntp -pl kubernetes-tests -am \
  -Dsurefire.failIfNoSpecifiedTests=false -Dtest=JobTest,BuildConfigTest test

Tests run: 17, Failures: 0, Errors: 0, Skipped: 1

Threat Model

If the APIs are allowed to accept additional URL parameters, then threat model should make it clear that the APIs are not safe to use with untrusted inputs.

Otherwise, the APIs that take as inputs specific URL params should not accept inputs with param separators. The fix below ensures that.

Note that the "Raw API path segments allow request retargeting" finding is similar in nature to this one.

Fix

Use URLUtils.URLBuilder for every query value instead of concatenating query text. This keeps attacker-supplied & and = characters inside the intended value.


Paweł Płatek from Trail of Bits in collaboration with OpenAI.

@GrosQuildu GrosQuildu changed the title Raw query values allow Kubernetes API option smuggling fix(kubernetes-client): raw query values allow Kubernetes API option smuggling Jun 26, 2026
@GrosQuildu
GrosQuildu marked this pull request as ready for review June 26, 2026 12:57
GrosQuildu added a commit to GrosQuildu/kubernetes-client that referenced this pull request Jun 26, 2026
@GrosQuildu
GrosQuildu force-pushed the ptp-51-query-option-smuggling branch from 3ba7596 to 34839a2 Compare June 26, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant