mirror of
https://github.com/aquasecurity/kube-hunter.git
synced 2026-02-19 20:39:53 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c17aa17096 | ||
|
|
4204879251 | ||
|
|
a746bd0eb1 | ||
|
|
b379e64314 | ||
|
|
00eb0dfa87 |
22
README.md
22
README.md
@@ -18,7 +18,8 @@ kube-hunter hunts for security weaknesses in Kubernetes clusters. The tool was d
|
||||
|
||||
**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/
|
||||
**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/
|
||||
_If you're interested in kube-hunter's integration with the Kubernetes ATT&CK Matrix [Continue Reading](#kuberentes-attck-matrix)_
|
||||
|
||||
**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](https://github.com/aquasecurity/kube-hunter/blob/main/CONTRIBUTING.md).
|
||||
|
||||
@@ -28,6 +29,7 @@ Table of Contents
|
||||
=================
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Kuberentes ATT&CK Matrix](#kuberentes-attck-matrix)
|
||||
- [Hunting](#hunting)
|
||||
- [Where should I run kube-hunter?](#where-should-i-run-kube-hunter)
|
||||
- [Scanning options](#scanning-options)
|
||||
@@ -48,7 +50,19 @@ Table of Contents
|
||||
- [Pod](#pod)
|
||||
- [Contribution](#contribution)
|
||||
- [License](#license)
|
||||
|
||||
|
||||
---
|
||||
## Kubernetes ATT&CK Matrix
|
||||
kube-hunter now supports the new format of the Kubernetes ATT&CK matrix.
|
||||
While kube-hunter's vulnerabilities are a collection of creative techniques designed to mimic an attacker in the cluster (or outside it)
|
||||
The Mitre's ATT&CK defines a more general standardised categories of techniques to do so.
|
||||
|
||||
You can think of kube-hunter vulnerabilities as small steps for an attacker, which follows the track of a more general technique he would aim for.
|
||||
Most of kube-hunter's hunters and vulnerabilities can closly fall under those techniques, That's why we moved to follow the Matrix standard.
|
||||
|
||||
_Some kube-hunter vulnerabities which we could not map to Mitre technique, are prefixed with the `General` keyword_
|
||||

|
||||
|
||||
## Hunting
|
||||
|
||||
### Where should I run kube-hunter?
|
||||
@@ -61,6 +75,7 @@ You can run kube-hunter directly on a machine in the cluster, and select the opt
|
||||
|
||||
You can also run kube-hunter in a pod within the cluster. This indicates how exposed your cluster would be if one of your application pods is compromised (through a software vulnerability, for example). (_`--pod` flag_)
|
||||
|
||||
|
||||
### Scanning options
|
||||
|
||||
First check for these **[pre-requisites](#prerequisites)**.
|
||||
@@ -141,7 +156,8 @@ Available dispatch methods are:
|
||||
* KUBEHUNTER_HTTP_DISPATCH_URL (defaults to: https://localhost)
|
||||
* KUBEHUNTER_HTTP_DISPATCH_METHOD (defaults to: POST)
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
### Advanced Usage
|
||||
#### Azure Quick Scanning
|
||||
When running **as a Pod in an Azure or AWS environment**, kube-hunter will fetch subnets from the Instance Metadata Service. Naturally this makes the discovery process take longer.
|
||||
To hardlimit subnet scanning to a `/24` CIDR, use the `--quick` option.
|
||||
|
||||
@@ -28,6 +28,7 @@ config = Config(
|
||||
k8s_auto_discover_nodes=args.k8s_auto_discover_nodes,
|
||||
service_account_token=args.service_account_token,
|
||||
kubeconfig=args.kubeconfig,
|
||||
enable_cve_hunting=args.enable_cve_hunting,
|
||||
)
|
||||
setup_logger(args.log, args.log_file)
|
||||
set_config(config)
|
||||
|
||||
@@ -21,6 +21,7 @@ class Config:
|
||||
- remote: Hosts to scan
|
||||
- report: Output format
|
||||
- statistics: Include hunters statistics
|
||||
- enable_cve_hunting: enables cve hunting, shows cve results
|
||||
"""
|
||||
|
||||
active: bool = False
|
||||
@@ -39,6 +40,7 @@ class Config:
|
||||
k8s_auto_discover_nodes: bool = False
|
||||
service_account_token: Optional[str] = None
|
||||
kubeconfig: Optional[str] = None
|
||||
enable_cve_hunting: bool = False
|
||||
|
||||
|
||||
_config: Optional[Config] = None
|
||||
|
||||
@@ -76,6 +76,12 @@ def parser_add_arguments(parser):
|
||||
|
||||
parser.add_argument("--active", action="store_true", help="Enables active hunting")
|
||||
|
||||
parser.add_argument(
|
||||
"--enable-cve-hunting",
|
||||
action="store_true",
|
||||
help="Show cluster CVEs based on discovered version (Depending on different vendors, may result in False Positives)",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--log",
|
||||
type=str,
|
||||
|
||||
@@ -62,7 +62,7 @@ class EventQueue(Queue):
|
||||
######################################################
|
||||
"""
|
||||
|
||||
def subscribe(self, event, hook=None, predicate=None):
|
||||
def subscribe(self, event, hook=None, predicate=None, is_register=True):
|
||||
"""
|
||||
The Subscribe Decorator - For Regular Registration
|
||||
Use this to register for one event only. Your hunter will execute each time this event is published
|
||||
@@ -74,12 +74,12 @@ class EventQueue(Queue):
|
||||
"""
|
||||
|
||||
def wrapper(hook):
|
||||
self.subscribe_event(event, hook=hook, predicate=predicate)
|
||||
self.subscribe_event(event, hook=hook, predicate=predicate, is_register=is_register)
|
||||
return hook
|
||||
|
||||
return wrapper
|
||||
|
||||
def subscribe_many(self, events, hook=None, predicates=None):
|
||||
def subscribe_many(self, events, hook=None, predicates=None, is_register=True):
|
||||
"""
|
||||
The Subscribe Many Decorator - For Multiple Registration,
|
||||
When your attack needs several prerequisites to exist in the cluster, You need to register for multiple events.
|
||||
@@ -99,12 +99,12 @@ class EventQueue(Queue):
|
||||
"""
|
||||
|
||||
def wrapper(hook):
|
||||
self.subscribe_events(events, hook=hook, predicates=predicates)
|
||||
self.subscribe_events(events, hook=hook, predicates=predicates, is_register=is_register)
|
||||
return hook
|
||||
|
||||
return wrapper
|
||||
|
||||
def subscribe_once(self, event, hook=None, predicate=None):
|
||||
def subscribe_once(self, event, hook=None, predicate=None, is_register=True):
|
||||
"""
|
||||
The Subscribe Once Decorator - For Single Trigger Registration,
|
||||
Use this when you want your hunter to execute only in your entire program run
|
||||
@@ -125,7 +125,8 @@ class EventQueue(Queue):
|
||||
|
||||
hook.__new__ = __new__unsubscribe_self
|
||||
|
||||
self.subscribe_event(event, hook=hook, predicate=predicate)
|
||||
self.subscribe_event(event, hook=hook, predicate=predicate, is_register=is_register)
|
||||
|
||||
return hook
|
||||
|
||||
return wrapper
|
||||
@@ -256,7 +257,9 @@ class EventQueue(Queue):
|
||||
self.hooks[event].append((hook, predicate))
|
||||
logging.debug("{} subscribed to {}".format(hook, event))
|
||||
|
||||
def subscribe_event(self, event, hook=None, predicate=None):
|
||||
def subscribe_event(self, event, hook=None, predicate=None, is_register=True):
|
||||
if not is_register:
|
||||
return
|
||||
if not self._register_hunters(hook):
|
||||
return
|
||||
|
||||
@@ -267,7 +270,9 @@ class EventQueue(Queue):
|
||||
else:
|
||||
self._register_hook(event, hook, predicate)
|
||||
|
||||
def subscribe_events(self, events, hook=None, predicates=None):
|
||||
def subscribe_events(self, events, hook=None, predicates=None, is_register=True):
|
||||
if not is_register:
|
||||
return False
|
||||
if not self._register_hunters(hook):
|
||||
return False
|
||||
|
||||
|
||||
@@ -166,7 +166,9 @@ class FromPodHostDiscovery(Discovery):
|
||||
return True
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.debug("Failed to connect AWS metadata server v1")
|
||||
return False
|
||||
except Exception:
|
||||
logger.debug("Unknown error when trying to connect to AWS metadata v1 API")
|
||||
return False
|
||||
|
||||
def is_aws_pod_v2(self):
|
||||
config = get_config()
|
||||
@@ -189,7 +191,9 @@ class FromPodHostDiscovery(Discovery):
|
||||
return True
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.debug("Failed to connect AWS metadata server v2")
|
||||
return False
|
||||
except Exception:
|
||||
logger.debug("Unknown error when trying to connect to AWS metadata v2 API")
|
||||
return False
|
||||
|
||||
def is_azure_pod(self):
|
||||
config = get_config()
|
||||
@@ -206,7 +210,9 @@ class FromPodHostDiscovery(Discovery):
|
||||
return True
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.debug("Failed to connect Azure metadata server")
|
||||
return False
|
||||
except Exception:
|
||||
logger.debug("Unknown error when trying to connect to Azure metadata server")
|
||||
return False
|
||||
|
||||
# for pod scanning
|
||||
def gateway_discovery(self):
|
||||
|
||||
@@ -3,7 +3,8 @@ from packaging import version
|
||||
|
||||
from kube_hunter.conf import get_config
|
||||
from kube_hunter.core.events import handler
|
||||
from kube_hunter.core.events.types import Vulnerability, Event, K8sVersionDisclosure
|
||||
|
||||
from kube_hunter.core.events.types import K8sVersionDisclosure, Vulnerability, Event
|
||||
from kube_hunter.core.types import (
|
||||
Hunter,
|
||||
KubectlClient,
|
||||
@@ -15,6 +16,7 @@ from kube_hunter.core.types import (
|
||||
from kube_hunter.modules.discovery.kubectl import KubectlClientEvent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
config = get_config()
|
||||
|
||||
|
||||
class ServerApiVersionEndPointAccessPE(Vulnerability, Event):
|
||||
@@ -199,7 +201,7 @@ class CveUtils:
|
||||
return vulnerable
|
||||
|
||||
|
||||
@handler.subscribe_once(K8sVersionDisclosure)
|
||||
@handler.subscribe_once(K8sVersionDisclosure, is_register=config.enable_cve_hunting)
|
||||
class K8sClusterCveHunter(Hunter):
|
||||
"""K8s CVE Hunter
|
||||
Checks if Node is running a Kubernetes version vulnerable to
|
||||
@@ -224,6 +226,7 @@ class K8sClusterCveHunter(Hunter):
|
||||
self.publish_event(vulnerability(self.event.version))
|
||||
|
||||
|
||||
# Removed due to incomplete implementation for multiple vendors revisions of kubernetes
|
||||
@handler.subscribe(KubectlClientEvent)
|
||||
class KubectlCVEHunter(Hunter):
|
||||
"""Kubectl CVE Hunter
|
||||
|
||||
@@ -16,6 +16,7 @@ class HTTPDispatcher:
|
||||
dispatch_url,
|
||||
json=report,
|
||||
headers={"Content-Type": "application/json"},
|
||||
verify=False
|
||||
)
|
||||
r.raise_for_status()
|
||||
logger.info(f"Report was dispatched to: {dispatch_url}")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# flake8: noqa: E402
|
||||
|
||||
from kube_hunter.conf import Config, set_config
|
||||
from kube_hunter.conf import Config, set_config, get_config
|
||||
|
||||
set_config(Config(active=True))
|
||||
|
||||
@@ -23,7 +23,9 @@ from kube_hunter.modules.hunting.apiserver import (
|
||||
from kube_hunter.modules.hunting.arp import ArpSpoofHunter
|
||||
from kube_hunter.modules.hunting.capabilities import PodCapabilitiesHunter
|
||||
from kube_hunter.modules.hunting.certificates import CertificateDiscovery
|
||||
from kube_hunter.modules.hunting.cves import K8sClusterCveHunter, KubectlCVEHunter
|
||||
|
||||
from kube_hunter.modules.hunting.cves import K8sClusterCveHunter
|
||||
from kube_hunter.modules.hunting.cves import KubectlCVEHunter
|
||||
from kube_hunter.modules.hunting.dashboard import KubeDashboard
|
||||
from kube_hunter.modules.hunting.dns import DnsSpoofHunter
|
||||
from kube_hunter.modules.hunting.etcd import EtcdRemoteAccess, EtcdRemoteAccessActive
|
||||
@@ -40,6 +42,8 @@ from kube_hunter.modules.hunting.mounts import VarLogMountHunter, ProveVarLogMou
|
||||
from kube_hunter.modules.hunting.proxy import KubeProxy, ProveProxyExposed, K8sVersionDisclosureProve
|
||||
from kube_hunter.modules.hunting.secrets import AccessSecrets
|
||||
|
||||
config = get_config()
|
||||
|
||||
PASSIVE_HUNTERS = {
|
||||
ApiServiceDiscovery,
|
||||
KubeDashboardDiscovery,
|
||||
@@ -56,7 +60,6 @@ PASSIVE_HUNTERS = {
|
||||
ApiVersionHunter,
|
||||
PodCapabilitiesHunter,
|
||||
CertificateDiscovery,
|
||||
K8sClusterCveHunter,
|
||||
KubectlCVEHunter,
|
||||
KubeDashboard,
|
||||
EtcdRemoteAccess,
|
||||
@@ -67,6 +70,9 @@ PASSIVE_HUNTERS = {
|
||||
AccessSecrets,
|
||||
}
|
||||
|
||||
# if config.enable_cve_hunting:
|
||||
# PASSIVE_HUNTERS.append(K8sClusterCveHunter)
|
||||
|
||||
ACTIVE_HUNTERS = {
|
||||
ProveAzureSpnExposure,
|
||||
AccessApiServerActive,
|
||||
|
||||
Reference in New Issue
Block a user