mirror of
https://github.com/aquasecurity/kube-hunter.git
synced 2026-08-23 22:26:23 +00:00
Merge branch 'master' into refactor_host_discovery
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
*aqua*
|
||||
venv/
|
||||
.vscode
|
||||
.coverage
|
||||
.idea
|
||||
|
||||
# Directory Cache Files
|
||||
|
||||
@@ -14,8 +14,11 @@ before_script:
|
||||
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
- pip install pytest coverage pytest-cov
|
||||
script:
|
||||
- python runtest.py
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
notifications:
|
||||
on_success: change
|
||||
on_failure: change # `always` will be the setting once code changes slow down
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||

|
||||
|
||||
[](https://travis-ci.org/aquasecurity/kube-hunter)
|
||||
[](https://codecov.io/gh/aquasecurity/kube-hunter)
|
||||
[](https://github.com/aquasecurity/kube-hunter/blob/master/LICENSE)
|
||||
[](https://microbadger.com/images/aquasec/kube-hunter "Get your own image badge on microbadger.com")
|
||||
|
||||
|
||||
|
||||
kube-hunter hunts for security weaknesses in Kubernetes clusters. The tool was developed to increase awareness and visibility for security issues in Kubernetes environments. **You should NOT run kube-hunter on a Kubernetes cluster that you don't own!**
|
||||
|
||||
**Run kube-hunter**: kube-hunter is available as a container (aquasec/kube-hunter), and we also offer a web site at [kube-hunter.aquasec.com](https://kube-hunter.aquasec.com) where you can register online to receive a token allowing you to see and share the results online. You can also run the Python code yourself as described below.
|
||||
|
||||
**Explore vulnerabilities**: The kube-hunter knowledge base includes articles about discoverable vulnerabilities and issues. When kube-hunter reports an issue, it will show its VID (Vulnerability ID) so you can look it up in the KB at https://aquasecurity.github.io/kube-hunter/
|
||||
|
||||
**Contribute**: We welcome contributions, especially new hunter modules that perform additional tests. If you would like to develop your modules please read [Guidelines For Developing Your First kube-hunter Module](src/README.md).
|
||||
|
||||
[](https://youtu.be/s2-6rTkH8a8?t=57s)
|
||||
@@ -146,7 +150,8 @@ By default, kube-hunter runs in interactive mode. You can also specify the scann
|
||||
### Pod
|
||||
This option lets you discover what running a malicious container can do/discover on your cluster. This gives a perspective on what an attacker could do if they were able to compromise a pod, perhaps through a software vulnerability. This may reveal significantly more vulnerabilities.
|
||||
|
||||
The `job.yaml` file defines a Job that will run kube-hunter in a pod, using default Kubernetes pod access settings.
|
||||
The example `job.yaml` file defines a Job that will run kube-hunter in a pod, using default Kubernetes pod access settings. (You may wish to modify this definition, for example to run as a non-root user, or to run in a different namespace.)
|
||||
|
||||
* Run the job with `kubectl create -f ./job.yaml`
|
||||
* Find the pod name with `kubectl describe job kube-hunter`
|
||||
* View the test results with `kubectl logs <pod name>`
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Information Disclosure]
|
||||
|
||||
## Issue description
|
||||
|
||||
The kubelet is is leaking container logs via the `/containerLogs` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
The kubelet is leaking container logs via the `/containerLogs` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
|
||||
## Remediation
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Information Disclosure]
|
||||
|
||||
## Issue description
|
||||
|
||||
The kubelet is is leaking information about runnig pods via the `/runningpods` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
The kubelet is leaking information about running pods via the `/runningpods` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
|
||||
## Remediation
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Remote Code Execution]
|
||||
|
||||
## Issue description
|
||||
|
||||
An attacker could run arbitrary commands on a container via the the kubelet's `/exec` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
An attacker could run arbitrary commands on a container via the kubelet's `/exec` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Remote Code Execution]
|
||||
|
||||
## Issue description
|
||||
|
||||
An attacker could run arbitrary commands on a container via the the kubelet's `/run` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
An attacker could run arbitrary commands on a container via the kubelet's `/run` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Remote Code Execution]
|
||||
|
||||
## Issue description
|
||||
|
||||
An attacker could read and write data from a pod via the the kubelet's `/portForward` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
An attacker could read and write data from a pod via the kubelet's `/portForward` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Remote Code Execution]
|
||||
|
||||
## Issue description
|
||||
|
||||
An attacker could attach to a running container via a websocket on the the the kubelet's `/attach` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
An attacker could attach to a running container via a websocket on the kubelet's `/attach` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Information Disclosure]
|
||||
|
||||
## Issue description
|
||||
|
||||
The kubelet is is leaking it's health information, which may contain sensitive information, via the `/healthz` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
The kubelet is leaking it's health information, which may contain sensitive information, via the `/healthz` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Information Disclosure]
|
||||
|
||||
## Issue description
|
||||
|
||||
The kubelet is is leaking system logs via the `/logs` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
The kubelet is leaking system logs via the `/logs` endpoint. This endpoint is exposed as part of the kubelet's debug handlers.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ categories: [Information Disclosure]
|
||||
|
||||
## Issue description
|
||||
|
||||
An open kubectl proxy was detected. `kubectl proxy` is a convenient tool to connent from a local machine into an application running in Kubernetes or to the Kubernetes API. This is common practice to browse for example the Kubernetes dashboard. Leaving an open proxy can be exploited by an attacker to gain access into your entire cluster.
|
||||
An open kubectl proxy was detected. `kubectl proxy` is a convenient tool to connect from a local machine into an application running in Kubernetes or to the Kubernetes API. This is common practice to browse for example the Kubernetes dashboard. Leaving an open proxy can be exploited by an attacker to gain access into your entire cluster.
|
||||
|
||||
## Remediation
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ parser.add_argument('--list', action="store_true", help="displays all tests in k
|
||||
parser.add_argument('--interface', action="store_true", help="set hunting of all network interfaces")
|
||||
parser.add_argument('--pod', action="store_true", help="set hunter as an insider pod")
|
||||
parser.add_argument('--quick', action="store_true", help="Prefer quick scan (subnet 24)")
|
||||
parser.add_argument('--ignore-downstream', action="store_true", help="Ignore patched kubernetes versions")
|
||||
parser.add_argument('--include-patched-versions', action="store_true", help="Don't skip patched versions when scanning")
|
||||
parser.add_argument('--cidr', type=str, help="set an ip range to scan, example: 192.168.0.0/16")
|
||||
parser.add_argument('--mapping', action="store_true", help="outputs only a mapping of the cluster's nodes")
|
||||
parser.add_argument('--remote', nargs='+', metavar="HOST", default=list(), help="one or more remote ip/dns to hunt")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import logging
|
||||
|
||||
# Supress logging from scapy
|
||||
# Suppress logging from scapy
|
||||
logging.getLogger("scapy.runtime").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("scapy.loading").setLevel(logging.CRITICAL)
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ parser.add_argument('--list', action="store_true", help="displays all tests in k
|
||||
parser.add_argument('--interface', action="store_true", help="set hunting of all interface network interfaces")
|
||||
parser.add_argument('--pod', action="store_true", help="set hunter as an insider pod")
|
||||
parser.add_argument('--quick', action="store_true", help="Prefer quick scan (subnet 24)")
|
||||
parser.add_argument('--ignore-downstream', action="store_true", help="Ignore patched kubernetes versions")
|
||||
parser.add_argument('--include-patched-versions', action="store_true", help="Don't skip patched versions when scanning")
|
||||
parser.add_argument('--cidr', type=str, help="set an ip range to scan, example: 192.168.0.0/16")
|
||||
parser.add_argument('--mapping', action="store_true", help="outputs only a mapping of the cluster's nodes")
|
||||
parser.add_argument('--remote', nargs='+', metavar="HOST", default=list(), help="one or more remote ip/dns to hunt")
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[aliases]
|
||||
test=pytest
|
||||
|
||||
# PyTest
|
||||
[tool:pytest]
|
||||
minversion = 2.9.1
|
||||
norecursedirs = .venv .vscode
|
||||
addopts = --cov=src
|
||||
testpaths = tests
|
||||
console_output_style = progress
|
||||
python_classes = Test*
|
||||
python_files = test_*.py
|
||||
python_functions = test_*
|
||||
filterwarnings = ignore::DeprecationWarning
|
||||
|
||||
# Coverage
|
||||
[coverage:report]
|
||||
# show missing lines numbers
|
||||
show_missing = True
|
||||
# Regexes for lines to exclude from consideration
|
||||
exclude_lines =
|
||||
# Have to re-enable the standard pragma
|
||||
pragma: no cover
|
||||
# Don't complain about missing debug-only code:
|
||||
def __repr__
|
||||
if self\.debug
|
||||
# Don't complain if tests don't hit defensive
|
||||
# assertion code:
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
# Don't complain if non-runnable code isn't run:
|
||||
if 0:
|
||||
if __name__ == .__main__.:
|
||||
+1
-1
@@ -25,7 +25,7 @@ When you write your module, you can decide on which Event to subscribe to, meani
|
||||
|
||||
-----------------------
|
||||
### Hunter Types
|
||||
There are three hunter types which you can implement: a `Hunter`, `ActiveHunter` and `Discovery`. Hunters just probe the state of a cluster, whereas ActiveHunter modules can attempt operations that could change the state of the cluster. Discovery is Hunter for discovery purposes only.
|
||||
There are three hunter types which you can implement: a `Hunter`, `ActiveHunter` and `Discovery`. Hunters just probe the state of a cluster, whereas ActiveHunter modules can attempt operations that could change the state of the cluster. Discovery is Hunter for discovery purposes only.
|
||||
##### Hunter
|
||||
Example:
|
||||
~~~python
|
||||
|
||||
@@ -27,7 +27,7 @@ class Event(object):
|
||||
# Event's logical location to be used mainly for reports.
|
||||
# If event don't implement it check previous event
|
||||
# This is because events are composed (previous -> previous ...)
|
||||
# and not inheritted
|
||||
# and not inherited
|
||||
def location(self):
|
||||
location = None
|
||||
if self.previous:
|
||||
@@ -77,7 +77,7 @@ class Vulnerability(object):
|
||||
UnauthenticatedAccess: "low"
|
||||
})
|
||||
|
||||
# TODO: make vid mandatry once migration is done
|
||||
# TODO: make vid mandatory once migration is done
|
||||
def __init__(self, component, name, category=None, vid=None):
|
||||
self.vid = vid
|
||||
self.component = component
|
||||
@@ -154,7 +154,7 @@ class ReportDispatched(Event):
|
||||
pass
|
||||
|
||||
|
||||
""" Core Vulnerabilites """
|
||||
""" Core Vulnerabilities """
|
||||
class K8sVersionDisclosure(Vulnerability, Event):
|
||||
"""The kubernetes version could be obtained from the {} endpoint """
|
||||
def __init__(self, version, from_endpoint, extra_info=""):
|
||||
|
||||
@@ -26,7 +26,7 @@ class ArpSpoofHunter(ActiveHunter):
|
||||
return ans[ARP].hwsrc if ans else None
|
||||
|
||||
def detect_l3_on_host(self, arp_responses):
|
||||
""" returns True for an existance of an L3 network plugin """
|
||||
""" returns True for an existence of an L3 network plugin """
|
||||
logging.debug("Attempting to detect L3 network plugin using ARP")
|
||||
unique_macs = list(set(response[ARP].hwsrc for _, response in arp_responses))
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ class K8sClusterCveHunter(Hunter):
|
||||
ServerApiClusterScopedResourcesAccess: ["1.13.9", "1.14.5", "1.15.2"]
|
||||
}
|
||||
for vulnerability, fix_versions in cve_mapping.items():
|
||||
if CveUtils.is_vulnerable(fix_versions, self.event.version, config.ignore_downstream):
|
||||
if CveUtils.is_vulnerable(fix_versions, self.event.version, not config.include_patched_versions):
|
||||
self.publish_event(vulnerability(self.event.version))
|
||||
|
||||
|
||||
@@ -173,5 +173,5 @@ class KubectlCVEHunter(Hunter):
|
||||
}
|
||||
logging.debug('Kubectl Cve Hunter determining vulnerable version: {}'.format(self.event.version))
|
||||
for vulnerability, fix_versions in cve_mapping.items():
|
||||
if CveUtils.is_vulnerable(fix_versions, self.event.version, config.ignore_downstream):
|
||||
if CveUtils.is_vulnerable(fix_versions, self.event.version, not config.include_patched_versions):
|
||||
self.publish_event(vulnerability(binary_version=self.event.version))
|
||||
|
||||
@@ -20,16 +20,7 @@ def test_K8sCveHunter():
|
||||
assert cve_counter == 2
|
||||
cve_counter = 0
|
||||
|
||||
# test complex version
|
||||
e = K8sVersionDisclosure(version="1.10.1-gke-1", from_endpoint="/version")
|
||||
h = K8sClusterCveHunter(e)
|
||||
h.execute()
|
||||
|
||||
time.sleep(0.01)
|
||||
assert cve_counter == 2
|
||||
cve_counter = 0
|
||||
|
||||
# test complex version
|
||||
# test patched version
|
||||
e = K8sVersionDisclosure(version="v1.13.6-gke.13", from_endpoint="/version")
|
||||
h = K8sClusterCveHunter(e)
|
||||
h.execute()
|
||||
|
||||
Reference in New Issue
Block a user