It might be necessary to apply some mitigation using external tools to give Dev teams more time to fix critical vulnerabilities, especially for large codebases. Employing Web Application Firewall could be such a temporary solution.

The article covers setting up a test Kubernetes environment with NGINX Ingress controller and ModSecurity WAF to block Spring RCE (CVE-2022-22965), known as Spring4Shell.

Kubernetes on macOS

One of the ways to get Kubernetes on a macOS machine is to enable it in the Docker desktop app.

If Kubernetes is up, the command kubectl get nodes should show a single node called docker-desktop.

Nginx ingress controller

We can install Nginx ingress controller using helm or create Kubernetes resources using kubectl:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace

or

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.2/deploy/static/provider/cloud/deploy.yaml

We have to wait until the ingress pod gets ready before proceeding:

kubectl wait \
  --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s

Test application

We are going to create a simple web server and an ingress resource.

kubectl create deployment modsecurity-demo --image=httpd --port=80
kubectl expose deployment modsecurity-demo
kubectl create ingress demo-localhost --class=nginx \
  --rule='modsecurity/*=modsecurity-demo:80'

To access our k8s application, we need to add the following record to the /etc/hosts file:

127.0.0.1 modsecurity

We need to keep up the tunnel to ingress pod for all the subsequent actions:

kubectl port-forward --namespace=ingress-nginx service/ingress-nginx-controller 8080:80

Let’s test our environment:

curl http://modsecurity:8080/

We should get <html><body><h1>It works!</h1></body></html>

ModSecurity Nginx module

To enable ModSecurity, we need to edit the ingress ConfigMap:

kubectl edit configmaps --namespace ingress-nginx ingress-nginx-controller

and put the following excerpt in the data key:

data:
  allow-snippet-annotations: "true"
  enable-modsecurity: "true"
  enable-owasp-modsecurity-crs: "true"

We could check that the configuration is ok by the command:

kubectl logs -f --namespace ingress-nginx \
  --selector=app.kubernetes.io/component=controller

The following logs are a good sign:

I0401 13:36:43.188153       8 controller.go:159] "Configuration changes detected, backend reload required"
I0401 13:36:43.457233       8 controller.go:176] "Backend successfully reloaded"

Now, we have enabled ModSecurity and The OWASP ModSecurity Core Rule Set.

For more complex solutions, consider using the following files:

  • /etc/nginx/modsecurity/modsecurity.conf;
  • /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf;

By default, ModSecurity works in the detection mode.

To block attacks, we override the config using the modsecurity-snippet key:

data:
  allow-snippet-annotations: "true"
  enable-modsecurity: "true"
  enable-owasp-modsecurity-crs: "true"
  modsecurity-snippet: |-
    SecRuleEngine On
    SecRequestBodyAccess On

The command curl 'http://modsecurity:8080/?param="><script>alert(1);</script>' should return

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

Spring4Shell exploit (CVE-2022-22965)

We might represent the Spring4Shell exploit by the following request:

curl 'http://modsecurity:8080/?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di'

That request will be blocked by rule 944130 of OWASP ModSecurity Core Rule Set as it contains certain Java classes listed here.

What if an attacker could use other classes?

The common thing among these types of attacks is the usage of Java Classloader (an object that is responsible for loading classes).

Let’s see how we can catch it.

ModSecurity rules

ModSecurity rules have the following syntax:

SecRule VARIABLES OPERATOR [ACTIONS]

Variables represent parts of the request analyzed. In this demo, we will employ ARGS and ARGS_NAMES variables.

  • ARGS contains all arguments, including the POST payload
  • ARGS_NAMES contains all request parameter names.

The operator is an expression specifying how and what to search in arguments. If the operator name is omitted, the string is interpreted as a regular expression.

We might use something like \.classLoader\. for our purpose.

We want to block matched requests and add some meaningful descriptions to logs so that we use the following action string:

log,deny,phase:2,id:76000001,severity:CRITICAL,msg:\'Possible Spring4Shell exploitation\'

Let’s put it all together in the ingress-nginx-controller ConfigMap as

data:
  allow-snippet-annotations: "true"
  enable-modsecurity: "true"
  enable-owasp-modsecurity-crs: "true"
  modsecurity-snippet: |-
    SecRuleEngine On
    SecRequestBodyAccess On
    SecRule ARGS|ARGS_NAMES "\.classLoader\." "log,\
      deny,\
      phase:2,\
      id:76000001,\
      severity:CRITICAL,\
      msg:\'Possible Spring4Shell exploitation\'"

Now, if we request

curl 'http://modsecurity:8080/?class.module.classLoader.resources.context.parent.pipeline.first.pattern=pupalupa'

or

curl -X POST http://modsecurity:8080/ -d 'class.module.classLoader.resources.context.parent.pipeline.first.pattern=pupalupa'

we will get the 403 Forbidden error.

Do we need to think about URL encoding bypasses and use some input preprocessing? No, ModSecurity automatically employs URL-decoding on request parameters.

But if you are deliberate in performing URL decoding twice, use the urlDecode function.

That is an oversimplified example to get a glimpse of the usage of ModSecurity WAF in Kubernetes. I highly recommend examining the ModSecurity documentation.

References