From 00eb0dfa87d8a1b2932f2fa0bfc67c57e7a4ed03 Mon Sep 17 00:00:00 2001 From: danielsagi Date: Sat, 16 Oct 2021 17:49:00 +0300 Subject: [PATCH] Switched CVE Hunting to optional & Minor core feature (#482) * Removed automatic registration of the k8s CVE hunter * Made CVE hunting optional, default set to not run --- kube_hunter/__main__.py | 1 + kube_hunter/conf/__init__.py | 2 ++ kube_hunter/conf/parser.py | 6 ++++++ kube_hunter/core/events/handler.py | 21 +++++++++++++-------- kube_hunter/modules/hunting/cves.py | 7 +++++-- tests/core/test_handler.py | 12 +++++++++--- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/kube_hunter/__main__.py b/kube_hunter/__main__.py index 91688fdd..b48fc39b 100755 --- a/kube_hunter/__main__.py +++ b/kube_hunter/__main__.py @@ -28,6 +28,7 @@ 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) diff --git a/kube_hunter/conf/__init__.py b/kube_hunter/conf/__init__.py index 983964cc..153654b5 100644 --- a/kube_hunter/conf/__init__.py +++ b/kube_hunter/conf/__init__.py @@ -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 diff --git a/kube_hunter/conf/parser.py b/kube_hunter/conf/parser.py index 0d829acb..15c85136 100644 --- a/kube_hunter/conf/parser.py +++ b/kube_hunter/conf/parser.py @@ -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, diff --git a/kube_hunter/core/events/handler.py b/kube_hunter/core/events/handler.py index 667e88ec..1a49cb0a 100644 --- a/kube_hunter/core/events/handler.py +++ b/kube_hunter/core/events/handler.py @@ -62,7 +62,7 @@ def __init__(self, num_worker=10): ###################################################### """ - 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 @@ def subscribe(self, event, hook=None, predicate=None): """ 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 @@ def subscribe_many(self, events, hook=None, predicates=None): """ 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 @@ def __new__unsubscribe_self(self, cls): 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 @@ def _register_hook(self, event, hook=None, predicate=None): 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 @@ def subscribe_event(self, event, hook=None, predicate=None): 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 diff --git a/kube_hunter/modules/hunting/cves.py b/kube_hunter/modules/hunting/cves.py index 7e8d8a92..b0740a39 100644 --- a/kube_hunter/modules/hunting/cves.py +++ b/kube_hunter/modules/hunting/cves.py @@ -3,7 +3,8 @@ 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.modules.discovery.kubectl import KubectlClientEvent logger = logging.getLogger(__name__) +config = get_config() class ServerApiVersionEndPointAccessPE(Vulnerability, Event): @@ -199,7 +201,7 @@ def is_vulnerable(fix_versions, check_version, ignore_downstream=False): 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 @@ def execute(self): 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 diff --git a/tests/core/test_handler.py b/tests/core/test_handler.py index 5ccc71e4..5cf1546d 100644 --- a/tests/core/test_handler.py +++ b/tests/core/test_handler.py @@ -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.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.proxy import KubeProxy, ProveProxyExposed, K8sVersionDisclosureProve from kube_hunter.modules.hunting.secrets import AccessSecrets +config = get_config() + PASSIVE_HUNTERS = { ApiServiceDiscovery, KubeDashboardDiscovery, @@ -56,7 +60,6 @@ ApiVersionHunter, PodCapabilitiesHunter, CertificateDiscovery, - K8sClusterCveHunter, KubectlCVEHunter, KubeDashboard, EtcdRemoteAccess, @@ -67,6 +70,9 @@ AccessSecrets, } +# if config.enable_cve_hunting: +# PASSIVE_HUNTERS.append(K8sClusterCveHunter) + ACTIVE_HUNTERS = { ProveAzureSpnExposure, AccessApiServerActive,