[et_pb_section bb_built=”1″][et_pb_row][et_pb_column type=”4_4″][et_pb_text]<\/p>\n
\n\t\t
Kubernetes has become incredibly popular, particularly for DevOps teams who want to automate deployment, horizontal scaling, A\/B testing, and failure tolerance for an application. I’ve had quite a bit of fun compromising Kubernetes clusters in penetration tests, even as I read about illicit Kubernetes compromises, like that of Tesla, Inc.\u00a0 <\/span>In this blog post, I’ll show you how the students in my 2018 training classes compromised and then hardened an intentionally-vulnerable Kubernetes cluster. In the next episode, we’ll attack and defend a multi-tenant Kubernetes cluster.<\/span><\/p>\n
We start by finding a vulnerable application, running in a Kubernetes cluster. Our target will be one of the Kubernetes documentation’s example applications, a Redis-backed Guestbook<\/a>, which has a small vulnerability, to which another has been added to give us remote code execution.<\/span><\/p>\n
Let\u2019s start by reading the source code (HTML and JavaScript) for this page. We see that this is an AJAX form, not a 1990’s style HTML form \u2013 clicking the submit button doesn\u2019t simply send a GET or POST request directly. Instead, the submit button calls JavaScript from the controller.js file to both pass the form in and update this page with output.<\/span><\/p>\n
<\/p>\n
This means we\u2019ll need to read controller.js to understand more about what\u2019s going on here. We pull down a copy of controller.js\u00a0<\/span>and see two functions.\u00a0 <\/span>Here\u2019s an excerpt from the first, which the submit button triggers:<\/p>\n
<\/p>\n
The JavaScript makes a GET request to guestbook.php, passing it these arguments:<\/span><\/p>\n
CMD: set<\/span>\r\nKEY: messages<\/span>\r\nVALUE: the item we entered in the form\u2019s text box<\/span>\r\n<\/pre>\n
This is where my co-worker Stan shouts out, “my Spidey Sense is tingling!”<\/p>\n
<\/p>\n
\u201cJay,\u201d says our cameo star, \u201cwhy does the GET request need to say what database key the message should go in? What would happen if you specified a different key?\u201d <\/span><\/p>\n
I tell Stan to be patient, but that I want to read the rest of the source before I go off checking that out.\u00a0 <\/span>Here\u2019s the other function in controller.js:<\/span><\/p>\n
<\/p>\n
This is the one that populates the messages on the Guestbook page, by running a request against guestbook.php with two of those three parameters:<\/span><\/p>\n
CMD: get<\/span>\r\nKEY: messages<\/span><\/pre>\nI bet Stan is right, that the server side code is letting the browser choose which Redis key to put data into. We\u2019ll come back to that, once we find something for which that bug is useful. I start to scratch my head, so I think of my enumeration basics. Well, we’ve got a web application, so let’s run OWASP’s DirBuster to find all of the pages and directories that we might not find just by following links.\u00a0 This will expand our view of the application’s attack surface. In my blog posts and webinars, I use stock Kali Linux (link: https:\/\/www.kali.org\/downloads\/<\/span><\/a>) so the reader can practice the same attacks, using the exact same tools as I\u2019m using. Kali has DirBuster set up wonderfully.<\/span><\/p>\n
We know about the guestbook.php page, so let’s investigate the status.php page.<\/span><\/p>\n
When we reload the page, we see this:<\/span><\/p>\n
Next, I set the Redis command key to pull down that Meterpreter, like so:<\/span><\/p>\n
Switching to my Metasploit console, I find that I\u2019ve caught a connection:<\/span><\/p>\n
kubectl get pod redis-master-55dv5f7567-ndjj9 -o yaml<\/span><\/p>\n
Success! Let\u2019s see which node that pod ended up on:<\/span><\/p>\n
Now, we can ssh into the node:<\/span><\/p>\n
And sudo su – to get a root shell!<\/span><\/p>\n
Defending this Kubernetes Cluster<\/h2>\n
You could certainly create a pod security policy to prevent pods from mounting directories from the node filesystems. You could also use a similar policy to prevent pods from running as root. You could also create a network policy that prevented Meterpreter shells from connecting back to you. But since the attack here was basically an exploration of over-privileged service accounts, let\u2019s look at that.<\/span><\/p>\n
Kubernetes’ primary authorization system is its RBAC<\/a> module.\u00a0 RBAC lets us define what subjects can take what actions on what types of objects, in what namespaces. There’s a good deal you can learn about this, but let’s use this practical example to see how easy it is.<\/p>\n
\n
- The role \u201cget-pods\u201d can execute the get verb on pods.<\/span><\/li>\n<\/ol>\n
\n
- The service account redis has the role \u201cget-pods.\u201d<\/span><\/li>\n<\/ol>\n
We\u2019ll create the \u201cget-pods\u201d role first:<\/span><\/p>\n
<\/p>\n
Now let\u2019s bind the \u201cget-pods\u201d role to the service account \u201credis\u201d by creating a role binding:<\/span><\/p>\n
<\/p>\n
The only remaining step here is to restart the redis-master and redis-slave deployments, with their ServiceAccount set to \u201credis\u201d:<\/span><\/p>\n
<\/p>\n
To test whether our defense was effective, we\u2019ll kubectl exec into the redis-master pod where we had put the kubectl binary and try to deploy a pod onto the cluster:<\/span><\/p>\n
<\/p>\n
Success!<\/span><\/p>\n
I\u2019ll be publishing the intentionally-vulnerable Kubernetes cluster I used in this demo soon, accessible on the site: www.bustakube.com<\/span><\/a> . Go practice this with it! <\/span><\/p>\n
\u00a0\u00a0<\/span><\/span><\/p>\n
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t<\/div> \n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t<\/div>\n\t\t<\/div> \n\t\t\n\t\t\t\n\t\t\t
<\/div>\n\t\t<\/div> \n\t\t\n\t\t\t\n\t\t<\/div>Jay Beale created two tools used by hundreds of thousands of individuals, companies and governments, Bastille Linux and the Center for Internet Security’s first Linux\/UNIX scoring tool. He has led training classes on Linux security at the Black Hat, CanSecWest, RSA, and IDG conferences, as well as in private corporate training, […]<\/p>\n","protected":false},"author":4,"featured_media":1796,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"[author] [author_image timthumb='on']https:\/\/www.inguardians.com\/wp-content\/uploads\/2017\/03\/jay-resize.jpg[\/author_image] [author_info]Jay Beale created two tools used by hundreds of thousands of individuals, companies and governments, Bastille Linux and the Center for Internet Security's first Linux\/UNIX scoring tool. He has led training classes on Linux security at the Black Hat, CanSecWest, RSA, and IDG conferences, as well as in private corporate training, since 2000. As an author, series editor and speaker, Jay has contributed to nine books and two columns and given roughly one hundred public talks. He is a co-founder, COO and CTO of the information security consulting company InGuardians.[\/author_info] [\/author] If you've read my past blog posts or watched my Linux Attack and Defense webinars, you'll see that one of my favorite hobbies is attacking a single-person capture the flag (CTF) virtual machine, then finding a proactive defense that would break my attack path. The critical point: even if you didn't know that your machine or application had vulnerabilities, you can break the exploits against them with hardening. In my\u00a0 \"Bust-a-Kube\" series of blog posts, I'm going to do this with Kubernetes<\/a>, the market-leading open-source container orchestration system.\u00a0 If you enjoy this, watch out for talks that I'm doing with other InGuardians folks at Hushcon 2018, RSA 2019, and online.<\/span><\/p>
We start by finding a vulnerable application, running in a Kubernetes cluster. Our target will be one of the Kubernetes documentation's example applications, a Redis-backed Guestbook<\/a>, which has a small vulnerability, to which another has been added to give us remote code execution.<\/span><\/p>
Compromising the Application<\/span><\/h2>
Let\u2019s start by reading the source code (HTML and JavaScript) for this page. We see that this is an AJAX form, not a 1990's style HTML form \u2013 clicking the submit button doesn\u2019t simply send a GET or POST request directly. Instead, the submit button calls JavaScript from the controller.js file to both pass the form in and update this page with output.<\/span><\/p>
<\/p>
This means we\u2019ll need to read controller.js to understand more about what\u2019s going on here. We pull down a copy of controller.js\u00a0<\/span>and see two functions.\u00a0 <\/span>Here\u2019s an excerpt from the first, which the submit button triggers:<\/p>
<\/p>
The JavaScript makes a GET request to guestbook.php, passing it these arguments:<\/span><\/p>
CMD: set<\/span>\r\nKEY: messages<\/span>\r\nVALUE: the item we entered in the form\u2019s text box<\/span>\r\n<\/pre><\/dl>
This is where my co-worker Stan shouts out, \"my Spidey Sense is tingling!\"<\/p>
<\/p>
\u201cJay,\u201d says our cameo star, \u201cwhy does the GET request need to say what database key the message should go in? What would happen if you specified a different key?\u201d <\/span><\/p>
I tell Stan to be patient, but that I want to read the rest of the source before I go off checking that out.\u00a0 <\/span>Here\u2019s the other function in controller.js:<\/span><\/p>
<\/p>
This is the one that populates the messages on the Guestbook page, by running a request against guestbook.php with two of those three parameters:<\/span><\/p>
CMD: get<\/span>\r\nKEY: messages<\/span><\/pre>I bet Stan is right, that the server side code is letting the browser choose which Redis key to put data into. We\u2019ll come back to that, once we find something for which that bug is useful. I start to scratch my head, so I think of my enumeration basics. Well, we've got a web application, so let's run OWASP's DirBuster to find all of the pages and directories that we might not find just by following links.\u00a0 This will expand our view of the application's attack surface. In my blog posts and webinars, I use stock Kali Linux (link: https:\/\/www.kali.org\/downloads\/<\/span><\/a>) so the reader can practice the same attacks, using the exact same tools as I\u2019m using. Kali has DirBuster set up wonderfully.<\/span><\/p>
We know about the guestbook.php page, so let's investigate the status.php page.<\/span><\/p>
When we reload the page, we see this:<\/span><\/p>
Next, I set the Redis command key to pull down that Meterpreter, like so:<\/span><\/p>
Switching to my Metasploit console, I find that I\u2019ve caught a connection:<\/span><\/p>
kubectl get pod redis-master-55dv5f7567-ndjj9 -o yaml<\/span><\/p>
Success! Let\u2019s see which node that pod ended up on:<\/span><\/p>
Now, we can ssh into the node:<\/span><\/p>
And sudo su - to get a root shell!<\/span><\/p>
Defending this Kubernetes Cluster<\/h2>
You could certainly create a pod security policy to prevent pods from mounting directories from the node filesystems. You could also use a similar policy to prevent pods from running as root. You could also create a network policy that prevented Meterpreter shells from connecting back to you. But since the attack here was basically an exploration of over-privileged service accounts, let\u2019s look at that.<\/span><\/p>
Kubernetes' primary authorization system is its RBAC<\/a> module.\u00a0 RBAC lets us define what subjects can take what actions on what types of objects, in what namespaces. There's a good deal you can learn about this, but let's use this practical example to see how easy it is.<\/p>
- The role \u201cget-pods\u201d can execute the get verb on pods.<\/span><\/li><\/ol>
- The service account redis has the role \u201cget-pods.\u201d<\/span><\/li><\/ol>
We\u2019ll create the \u201cget-pods\u201d role first:<\/span><\/p>
<\/p>
Now let\u2019s bind the \u201cget-pods\u201d role to the service account \u201credis\u201d by creating a role binding:<\/span><\/p>
<\/p>
The only remaining step here is to restart the redis-master and redis-slave deployments, with their ServiceAccount set to \u201credis\u201d:<\/span><\/p>
<\/p>
To test whether our defense was effective, we\u2019ll kubectl exec into the redis-master pod where we had put the kubectl binary and try to deploy a pod onto the cluster:<\/span><\/p>
<\/p>
Success!<\/span><\/p>
I\u2019ll be publishing the intentionally-vulnerable Kubernetes cluster I used in this demo soon, accessible on the site: www.bustakube.com<\/span><\/a> . Go practice this with it! <\/span><\/p>