There’s only one way to secure a Kubernetes cluster from an application stack (Deployments, Pods, ConfigMaps, Secrets, etc etc.) perspective, and that’s to see and understand an exploit.
Otherwise, it’s all just theoretical, which is why it’s so important for everyone in security (including blue team) to understand how attacks work, pentesting, and vulnerabilities.
In this blog, you’ll learn how to purposely exploit a Kubernetes cluster with an application stack that has many vulnerabilities by design.
WARNING: Do NOT run this in production or in a cluster at work unless you are given direct permission to do so, and it should be done in a Dev environment that has no connection to production and very little external capabilities.
Prerequisites
To follow along with this blog post from a hands-on perspective, you will need:
- A Kubernetes cluster that is not in your work and/or production environment. This should be a test cluster.
Securing What You Don’t Know Never Works
The majority of organizations that implement security practices implement them based on what could happen or what has happened. For example, if you use a too like Crowdstrike for endpoint protection or a SIEM solution, you’re protecting against what could happen. Most security tools will use best practices from places like OWASP and implement scanning capabilities to scan against security databases like MITRE, NIST, NVD, and CIS.
All of this is great, but new attacks are found all of the time, and more importantly, what happens if one of these attacks get through your security software? In both scenarios, how do you know how to stop the attack or even understand the attack if you haven’t done a particular attack yourself?
Taking it a step further, you cannot secure what you don’t know. If security engineers are attempting to secure a Kubernetes cluster, but they don’t know Kubernetes and how the various APIs work, it’ll never be at 100% defense and there will always be attack paths.
With both of these points taken into account, it’s the perfect reason to purposely exploit a Kubernetes cluster and run vulnerable workloads. That way, you can learn the process.
Deploying Vulnerable Applications
There’s one particular vulnerable application stack that was built for testing purposes to learn security within Kubernetes.
That application stack is called kubegoat.
Kubernetes Goat deploys several vulnerable Kubernetes Manifests that contain Pods, Secrets, ConfigMaps, RBAC, Services, and various other Kubernetes resources.
To install Kubegoat, first clone the repo from GitHub.
git clone https://github.com/madhuakula/kubernetes-goat.git
Next, cd
into the kubernetes-goat
directory.
cd kubernetes-goat
Modify the permissions to be able to run the script locally.
chmod +x setup-kubernetes-goat.sh
Run the script. The script consists of a bunch of kubectl apply
commands to deploy all of the Kubernetes Resources.
bash setup-kubernetes-goat.sh
You should now see that the resources have deployed.
What Tools Can Catch The Attacks?
Now that the vulnerable application is deployed, let’s use a couple of tools to see if they can catch the vulnerabilities.
Burp Suite
The first thing that I noticed about this vulnerable application stack is there aren’t a lot of frontend-based/webapp-based applications, so the vulnerabilities are going to come from backend, middleware, and misconfigurations like poor RBAC standards, secret issues, containers with too many permissions (especially to hostPath), and database misconfigurations.
However, there is one frontend - the Kubernetes Goat Instructions! Let’s scan that to see if it’s vulnerable.
After scanning, we can see four issues.
The issues contain Clickjacking vulnerabilities, unencrypted network traffic between k8s resources, and two issues that sit at robots.txt
. Luckily, robots.txt
, although a valid URL, was not exposed and a 404
was received.
💡 robots.txt is sometimes exposed at the end of a URL and is a text file that instructs automated web bots on how to crawl and/or index a website. It can potentially show the path to an administrator (like /admin-panel
), which would allow you to know the path to where to get admin access and then you could attempt admin access via some type of dictionary or brute force attack.
Kube-Bench
Aqua Security kube-bench is a great tool, but it only scans the cluster itself for issues (as in, if it was deployed correctly, not resources.
That means that at this stage, kube-bench can not help us in our efforts to scan Kubernetes Resources (like Pods) for potential vulnerabilities.
Kubescape
When it comes to all things Kubernetes security, Kubescape continues to be the de facto standard.
From a UI perspective, it’ll tell you every vulnerability that your cluster and application stack has based on the most popular security databases including:
- NIST
- MITRE
- CIS
- NVD
Within the UI, you can go down to the Pod level and see the exact vulnerability with remediation steps.
If you want to use the UI, however, it’s paid. There’s a 21 day free trial, but that’s it. However, the Kubescape CLI is open-source.
If you download the Kubescape CLI, you can run the following command to see what’s available:
kubescape scan --help
You’ll see an output that shows you can scan Kubernetes resources and images (kubescape scan workload
)
Available Commands:
control The controls you wish to use. Run 'kubescape list controls' for the list of supported controls
framework The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks
image Scan an image for vulnerabilities
workload Scan a workload for misconfigurations and image vulnerabilities
Kubelinter
When you Lint code, it means you’re checking the quality posture of the code. Is it following best practices? Are there any known misconfigurations? Sometimes, you can use them for security scanning as well.
Kubelinter is one of those tools.
To install it, you can use the package manager for your architecture or go directly to the binary. Below is a Brew example.
brew install kube-linter
Once installed, cd
into the Scenarios folder of Kubernetes Goat.
cd kubernetes-goat/scenarios
Run the Linter against one of the directories in scenarios
.
kube-linter lint batch-check
You’ll get an output similar to the below. Notice how the second output is shows a security violation for the SecurityContext
, which indicates that Kubelinter is also scanning for security vulnerabilities within Kubernetes YAML, indicating some SAST capabilities.
KubeLinter 0.6.8
/Users/michael/gitrepos/csec_prod_examples/kubernetes/kubegoat/kubernetes-goat/scenarios/batch-check/job.yaml: (object: <no namespace>/batch-check-job batch/v1, Kind=Job) The container "batch-check" is using an invalid container image, "madhuakula/k8s-goat-batch-check". Please use images that are not blocked by the `BlockList` criteria : [".*:(latest)$" "^[^:]*$" "(.*/[^:]+)$"] (check: latest-tag, remediation: Use a container image with a specific tag other than latest.)
/Users/michael/gitrepos/csec_prod_examples/kubernetes/kubegoat/kubernetes-goat/scenarios/batch-check/job.yaml: (object: <no namespace>/batch-check-job batch/v1, Kind=Job) container "batch-check" does not have a read-only root file system (check: no-read-only-root-fs, remediation: Set readOnlyRootFilesystem to true in the container securityContext.)
💡 SAST (Static Application Security Testing) is essentially a security-based linter. Sometimes, linter tools have security capabilities built in, which makes them “SAST-like”. Aside form Kubelinter, another good example is Golangs (Go) gosec
tool.
Kubesec
Here’s another linter-like tool that can help on your journey. It’s called Kubesec.
To download Kubesec, go directory to the binary.
wget https://github.com/controlplaneio/kubesec/releases/download/v2.14.1/kubesec_darwin_arm64.tar.gz
Untar the binary.
tar -xvf kubesec_darwin_arm64.tar.gz
Run a scan against one of the directories in the scenarios
directory.
./kubesec scan scenarios/build-code/*
You’ll see an output similar to the one below.
You can also automate this process with a script instead of having to scan each directory manually. Here’s an example PowerShell script.
function exeForAll {
[cmdletbinding(SupportsShouldProcess, ConfirmImpact='medium')]
param(
[parameter(Position = 0, HelpMessage="Path to where Kubernetes-Goat/Scenarios exists")]
[string]$directory = "/Users/michael/gitrepos/csec_prod_examples/kubernetes/kubegoat/kubernetes-goat/scenarios",
[parameter(Position = 1, HelpMessage="Path to where the Kubesec binary exists")]
[string]$kubesec = "/Users/michael/gitrepos/csec_prod_examples/kubernetes/kubegoat/kubernetes-goat/kubesec1"
)
$env:Path=$kubesec
Set-Location -Path $directory/"batch-check/"
$child = Get-ChildItem
for ($i -in $child) {
kubesec scan $i*
Set-Location ..
break
}
Set-Location -Path "health-check/"
$child = Get-ChildItem
for ($i -in $child) {
kubesec scan $i*
Set-Location ..
break
}
}
However, depending on what you scan, you may also see outputs like the two below.
[
{
"object": "Job/batch-check-job.default",
"valid": true,
"fileName": "scenarios/batch-check/job.yaml",
"message": "This resource kind is not supported by kubesec",
"score": 0,
"scoring": {}
}
]
{
"object": "Service/build-code-service.default",
"valid": true,
"fileName": "scenarios/build-code/deployment.yaml",
"message": "This resource kind is not supported by kubesec",
"score": 0,
"scoring": {}
}
This indicates that Kubesec doesn’t scan all Kubernetes Resources, and therefore doesn’t make much sense to use at the time of writing this in comparison to Kubelinter.