{"id":195,"date":"2019-09-26T12:07:00","date_gmt":"2019-09-26T12:07:00","guid":{"rendered":"https:\/\/certitude.consulting\/blog\/?p=195"},"modified":"2021-01-26T12:10:52","modified_gmt":"2021-01-26T12:10:52","slug":"docker-kubectl-exec-did-not-respect-no-new-privileges-and-allowprivilegeescalation","status":"publish","type":"post","link":"https:\/\/certitude.consulting\/blog\/en\/docker-kubectl-exec-did-not-respect-no-new-privileges-and-allowprivilegeescalation\/","title":{"rendered":"docker\/kubectl exec did not respect no-new-privileges and allowPrivilegeEscalation"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"843\" height=\"633\" src=\"https:\/\/certitude.consulting\/blog\/wp-content\/uploads\/2021\/01\/container.jpg\" alt=\"\" class=\"wp-image-234\" srcset=\"https:\/\/certitude.consulting\/blog\/wp-content\/uploads\/2021\/01\/container.jpg 843w, https:\/\/certitude.consulting\/blog\/wp-content\/uploads\/2021\/01\/container-300x225.jpg 300w, https:\/\/certitude.consulting\/blog\/wp-content\/uploads\/2021\/01\/container-768x577.jpg 768w\" sizes=\"auto, (max-width: 843px) 100vw, 843px\" \/><\/figure><\/div>\n\n\n\n<p><strong>TL;DR: There was a bug in docker, which made docker exec not respect the no-new-privileges security option. This issue also impacted the allowPrivilegeEscalation=false setting in Kubernetes. This could have been abused by attackers in certain scenarios. The bug has recently been resolved (confirmed in docker version 18.09.7). So make sure to update docker!<\/strong><\/p>\n\n\n\n<p>I recently performed a Kubernets pentest and encountered some strange behavior that only made sense after some investigation. Unfortunately this is not well documented in the Kubernetes documentation at the time of this writing.<\/p>\n\n\n\n<p>A client wanted to secure his pods and prevent them from running with root. So they set the securityContext and specified a non-privileged user via the runAsUser\/runAsGroup configuration. However, this setting is not bulletproof, since a compromised process might escalate its privileges at runtime, e.g. with SUID root binaries. I created a PoC for it and it worked as expected &#8211; I got root despite a non-privileged user being specified in the securityContext. But this is not really the scope of this post.<\/p>\n\n\n\n<p>But next I wanted to give the client recommendations about how to resolve this issue and if you know the Kubernetes securityContext settings, you are probably aware of the allowPrivilegeEscalation flag. This flag is supposed to prevent exactly what I did in my PoC. From the Kubernetes documentation:<\/p>\n\n\n\n<p><em>AllowPrivilegeEscalation: Controls whether a process can gain more privileges than its parent process.<\/em><\/p>\n\n\n\n<p>So what I did was to set this setting to false, kubectl-exec into by container, try to escalate again and check my uid. I would have expected that privilege escalation would not work now and that I am still a non-privileged user. But here is what I got after executing my SUID root shell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user@kubernetes-master:~$ kubectl run urootshell --image=impidio\/urootshell:0.2 --replicas=1 --overrides='{\"spec\": {\"template\": {\"spec\": {\"containers\": &#91;{\"name\": \"urootshell\", \"image\": \"impidio\/urootshell:0.2\", \"command\": &#91;\"\/bin\/sh\", \"-c\", \"sleep 60m\"], \"securityContext\": {\"allowPrivilegeEscalation\": false} }]}}}}'\ndeployment.apps\/urootshell created\nuser@kubernetes-master:~$ kubectl get pod urootshell-56c65c6666-kgjdr -o yaml | grep allowPriv\n      allowPrivilegeEscalation: false\nuser@kubernetes-master:~$ kubectl exec -it urootshell-56c65c6666-kgjdr -c urootshell -- bash\nuser@urootshell-56c65c6666-kgjdr:~$ id\nuid=1000(user) gid=1000(user) groups=1000(user)\nuser@urootshell-56c65c6666-kgjdr:~$ ls -l \/bin\/rootshell\n-rwsrwxrwx 1 root root 8352 Sep  5 14:42 \/bin\/rootshell\nuser@urootshell-56c65c6666-kgjdr:~$ \/bin\/rootshell\n# id\nuid=0(root) gid=1000(user) groups=1000(user)<\/code><\/pre>\n\n\n\n<p>Still root. Strange. Does the allowPrivilegeEscalation setting not work?<\/p>\n\n\n\n<p>So I confirmed that the allowPrivilegeEscalation=false gets passed on to the docker-runtime for the container in my Kubernetes pod:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user@kubernetes-node1:~$ docker inspect 32ee4c9105a4\n...\n\"SecurityOpt\": &#91;\n                \"no-new-privileges\",\n                \"seccomp=unconfined\"\n            ],\n...<\/code><\/pre>\n\n\n\n<p>So everything looks correctly applied. So why does it not prevent me from escalating? So next, I looked at the processes in the pod directly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user@kubernetes-master:~$ kubectl exec -it urootshell-56c65c6666-kgjdr -c urootshell -- bash\nuser@urootshell-56c65c6666-kgjdr:~$ grep NoNewPrivs \/proc\/1\/status\nNoNewPrivs:     1\nuser@urootshell-56c65c6666-kgjdr:~$ grep NoNewPrivs \/proc\/$$\/status\nNoNewPrivs:     0\nuser@urootshell-56c65c6666-kgjdr:~$ \/bin\/rootshell\n# id\nuid=0(root) gid=1000(user) groups=1000(user)\n# grep NoNewPrivs \/proc\/$$\/status\nNoNewPrivs:     0<\/code><\/pre>\n\n\n\n<p>Interesting, so the actual container process had NoNewPrivs correctly applied at kernel-level, but my kubectl-exec bash not and so the same for my SUID root shell (rootshell). To me this looked like a bug or even vulnerability. But is it in Kubernetes or Docker?<\/p>\n\n\n\n<p>So I tried if this also happens in Docker. First I ran a docker container interactively and tried to escalate with the no-new-privileges flag set:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user@kubernetes-node1:~$ docker run -it --security-opt no-new-privileges impidio\/urootshell:0.4\n$ id\nuid=1000(user) gid=1000(user) groups=1000(user)\n$ \/bin\/rootshell\n$ id\nuid=1000(user) gid=1000(user) groups=1000(user)<\/code><\/pre>\n\n\n\n<p>Seems to work correctly. However, when I used docker exec to go into the same container, it was again possible to escalate:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user@kubernetes-node1:~$ docker exec -it fd9dce6682b8 \/bin\/sh\n$ id\nuid=1000(user) gid=1000(user) groups=1000(user)\n$ \/bin\/rootshell\n# id\nuid=0(root) gid=1000(user) groups=1000(user)<\/code><\/pre>\n\n\n\n<p>In fact not a Kubernetes, but a docker exec issue!<\/p>\n\n\n\n<p>After some further research, I came across <a href=\"https:\/\/github.com\/kubernetes\/kubernetes\/issues\/73275\" target=\"_blank\" rel=\"noreferrer noopener\">this bug-report<\/a> and clicked through a few references until I ended up <a href=\"https:\/\/github.com\/moby\/moby\/pull\/36641\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a> [2]. So this described exactly my problem and seems to have been resolved quite recently.<\/p>\n\n\n\n<p>So I upgraded to the latest docker version available in Ubuntu, which was 18.09.7 at the time of this writing to confirm it this issue has been resolved and it is!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>root@ubuntu:\/home\/user# docker exec -it 0d sh\n$ id\nuid=1000(user) gid=1000(user) groups=1000(user)\n$ \/bin\/rootshell\n$ id\nuid=1000(user) gid=1000(user) groups=1000(user)<\/code><\/pre>\n\n\n\n<p>So hopefully something we won\u2019t need to worry about anymore in future!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR: There was a bug in docker, which made docker exec not respect the no-new-privileges security option. This issue also impacted the allowPrivilegeEscalation=false setting in Kubernetes. This could have been abused by attackers in certain scenarios. The bug has recently been resolved (confirmed in docker version 18.09.7). So make sure to update docker! I recently [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":234,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60,103],"tags":[83,107,108,84],"class_list":["post-195","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technical-analysis","category-vulnerability-research-en","tag-docker","tag-kubernetes","tag-privilege-escalation","tag-vulnerability"],"_links":{"self":[{"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/posts\/195","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/comments?post=195"}],"version-history":[{"count":3,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/posts\/195\/revisions"}],"predecessor-version":[{"id":242,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/posts\/195\/revisions\/242"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/media\/234"}],"wp:attachment":[{"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/media?parent=195"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/categories?post=195"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/certitude.consulting\/blog\/wp-json\/wp\/v2\/tags?post=195"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}