Improper Access Control
- Kubernetes
- Kubernetes v1.12
- OS: Ubuntu 18.04 LTS
- Kubernetes Version: v1.12
- Tools: minicube
A list of allowed origins can be supplied to the Kubernetes apiserver at runtime to support Cross-Origin Resource Sharing (CORS). Documentation states that this list "can be a regular expression to support subdomain matching"; however, every item in the list is treated as a regular expression in the CORS request handling middleware. Further, documentation and test cases do not contain examples of safe regular expressions.
Because of this, if a user provides a valid origin, such as "https://www.example.com", it would be possible for an invalid origin to bypass the intended control, using an origin such as "https://www.example.com.evil.com". For the user to protect themselves from this issue, they would need to supply a valid regular expression that contains the meta-characters needed to mark the end of the provided origin (i.e. "$").
The impact of this issue would be dependent on the authentication mechanisms in use. For example, cookie or basic authentication would allow an attacker to submit requests and read responses within the context of the application consumer; however, the use of a JWT or something similar would likely not have an impact, as these authentication mechanisms could not be abused without an attacker knowing the user's credentials. Some far-fetched scenarios could include the use of easy-to-guess credentials and an application and domain that is on a network location that an attacker could not contact directly.
This should be resolved to prevent any future issues in the Kubernetes ecosystem.
A proper solution should protect users from themselves. There are a couple of routes that could be taken to resolve this issue.
- Adjust documentation to reflect that all items are treated as a regular expression, and that items not containing and matching end meta-character could result in security issues. Additionally, adjust test cases and documentation to show secure examples.
- Append a matching end meta-character to each item supplied from the user.
- Attempt to identify when a regular expression is provided and only treat those as regular expressions.
This vulnerability was identified by Tom Steele of Atredis Partners.
- https://www.atredis.com/disclosure/
- https://github.com/kubernetes/kubernetes/blob/7f23a743e8c23ac6489340bbb34fa6f1d392db9d/staging/src/k8s.io/apiserver/pkg/server/options/server_run_options.go
- https://github.com/kubernetes/kubernetes/blob/a3ccea9d8743f2ff82e41b6c2af6dc2c41dc7b10/staging/src/k8s.io/apiserver/pkg/server/filters/cors.go
- https://groups.google.com/forum/#!topic/kubernetes-security-announce/MfFKo_9YAFk
- kubernetes/kubernetes#71669
- 2018-11-19: Atredis Partners sends advisory to Kubernetes Product Security Team.
- 2018-11-19: Kubernetes Product Security Team acknowleges the issue
- 2018-12-03: Kubernetes Product Security Team publishes information about the issue and a mitigation: https://groups.google.com/forum/#!topic/kubernetes-security-announce/MfFKo_9YAFk
- 2018-12-20: Atredis Partners publishes this advisory
The following startup options can be supplied to minikube
for testing:
# minikube start --extra-config=apiserver.cors-allowed-origins=https://example.com
As seen below, if a request is made from an un-allowed origin, there are no Access-Control-* headers returned:
# curl -ki https://localhost:8443 -H 'Origin: https://evil.com'
HTTP/1.1 403 Forbidden
Content-Type: application/json
X-Content-Type-Options: nosniff
Date: Tue, 13 Nov 2018 06:26:32 GMT
Content-Length: 233
Next, a request is made with from an allowed origin. As expected, Access-Control-* headers are returned:
# curl -ki https://localhost:8443 -H 'Origin: https://example.com'
HTTP/1.1 403 Forbidden
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Requested-With, If-Modified-Since
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, PATCH
Access-Control-Allow-Origin: https://example.com
Access-Control-Expose-Headers: Date
Content-Type: application/json
Finally, a request is made again from an un-allowed origin, but one that prepends an allowed origin as a sub-domain. This time, Access-Control-* headers are returned:
# curl -ki https://localhost:8443 -H 'Origin: https://example.com.evil.com'
HTTP/1.1 403 Forbidden
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Requested-With, If-Modified-Since
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, PATCH
Access-Control-Allow-Origin: https://example.com.evil.com
Access-Control-Expose-Headers: Date
Content-Type: application/json