From c06b94f558dda58f6ad5199d718f5bddc05af644 Mon Sep 17 00:00:00 2001 From: Idan Revivo Date: Sun, 3 Mar 2019 18:53:35 +0200 Subject: [PATCH 01/14] moved CVE_2018_1002105 to generic cvehunter --- src/modules/hunting/{CVE_2018_1002105.py => cvehunter.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/modules/hunting/{CVE_2018_1002105.py => cvehunter.py} (100%) diff --git a/src/modules/hunting/CVE_2018_1002105.py b/src/modules/hunting/cvehunter.py similarity index 100% rename from src/modules/hunting/CVE_2018_1002105.py rename to src/modules/hunting/cvehunter.py From 1d258f7447e52d5e74d102c130ca081f77bd776c Mon Sep 17 00:00:00 2001 From: Idan Revivo Date: Sun, 3 Mar 2019 18:57:12 +0200 Subject: [PATCH 02/14] added support for new Vulnerability CVE-2019-1002100 --- src/core/types.py | 2 + src/modules/hunting/cvehunter.py | 105 ++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 28 deletions(-) diff --git a/src/core/types.py b/src/core/types.py index 9a404fd..0e13e64 100644 --- a/src/core/types.py +++ b/src/core/types.py @@ -48,5 +48,7 @@ class AccessRisk(object): class PrivilegeEscalation(KubernetesCluster): name = "Privilege Escalation" +class DenialOfService(object): + name = "Denial of Service" from events import handler # import is in the bottom to break import loops \ No newline at end of file diff --git a/src/modules/hunting/cvehunter.py b/src/modules/hunting/cvehunter.py index ae2b4b1..1f035f1 100644 --- a/src/modules/hunting/cvehunter.py +++ b/src/modules/hunting/cvehunter.py @@ -7,16 +7,28 @@ import ast from ...core.events import handler from ...core.events.types import Vulnerability, Event from ..discovery.apiserver import ApiServer -from ...core.types import Hunter, ActiveHunter, KubernetesCluster, RemoteCodeExec, AccessRisk, InformationDisclosure, PrivilegeEscalation +from ...core.types import Hunter, ActiveHunter, KubernetesCluster, RemoteCodeExec, AccessRisk, InformationDisclosure, \ + PrivilegeEscalation, DenialOfService """ Vulnerabilities """ -class ServerApiVersionEndPointAccess(Vulnerability, Event): - """ Node is vulnerable to critical CVE-2018-1002105 """ + + +class ServerApiVersionEndPointAccessPE(Vulnerability, Event): + """Node is vulnerable to critical CVE-2018-1002105""" def __init__(self, evidence): Vulnerability.__init__(self, KubernetesCluster, name="Critical Privilege Escalation CVE", category=PrivilegeEscalation) self.evidence = evidence + +class ServerApiVersionEndPointAccessDos(Vulnerability, Event): + """Node is vulnerable to critical CVE-2019-1002100""" + + def __init__(self, evidence): + Vulnerability.__init__(self, KubernetesCluster, name="Medium Denial of Service CVE", category=DenialOfService) + self.evidence = evidence + + # Passive Hunter @handler.subscribe(ApiServer) class IsVulnerableToCVEAttack(Hunter): @@ -30,29 +42,6 @@ class IsVulnerableToCVEAttack(Hunter): self.api_server_evidence = '' self.k8sVersion = '' - def access_api_server_version_end_point(self): - logging.debug(self.event.host) - logging.debug('Passive Hunter is attempting to access the API server /version end point using the pod\'s service account token') - try: - res = requests.get("{path}/version".format(path=self.path), - headers=self.headers, verify=False) - self.api_server_evidence = res.content - resDict = ast.literal_eval(res.content) - version = resDict["gitVersion"].split('.') - first_two_minor_digists = eval(version[1]) - last_two_minor_digists = eval(version[2]) - - if first_two_minor_digists == 10 and last_two_minor_digists < 11: - return True - elif first_two_minor_digists == 11 and last_two_minor_digists < 5: - return True - elif first_two_minor_digists == 12 and last_two_minor_digists < 3: - return True - elif first_two_minor_digists < 10: - return True - except (requests.exceptions.ConnectionError, KeyError): - return False - def get_service_account_token(self): logging.debug(self.event.host) logging.debug('Passive Hunter is attempting to access pod\'s service account token') @@ -65,8 +54,68 @@ class IsVulnerableToCVEAttack(Hunter): except IOError: # Couldn't read file return False + def get_api_server_version_end_point(self): + logging.debug(self.event.host) + logging.debug('Passive Hunter is attempting to access the API server version end point using the pod\'s service account token') + try: + res = requests.get("{path}/version".format(path=self.path), + headers=self.headers, verify=False) + self.api_server_evidence = res.content + resDict = ast.literal_eval(res.content) + version = resDict["gitVersion"].split('.') + first_two_minor_digits = eval(version[1]) + last_two_minor_digits = eval(version[2]) + return [first_two_minor_digits, last_two_minor_digits] + + except (requests.exceptions.ConnectionError, KeyError): + return None + + def check_cve_2018_1002105(self, api_version): + first_two_minor_digists = api_version[0] + last_two_minor_digists = api_version[1] + + if first_two_minor_digists == 10 and last_two_minor_digists < 11: + return True + elif first_two_minor_digists == 11 and last_two_minor_digists < 5: + return True + elif first_two_minor_digists == 12 and last_two_minor_digists < 3: + return True + elif first_two_minor_digists < 10: + return True + + return False + + def check_cve_2019_1002100(self, api_version): + """ + Kubernetes v1.0.x-1.10.x + Kubernetes v1.11.0-1.11.7 (fixed in v1.11.8) + Kubernetes v1.12.0-1.12.5 (fixed in v1.12.6) + Kubernetes v1.13.0-1.13.3 (fixed in v1.13.4) + """ + + first_two_minor_digists = api_version[0] + last_two_minor_digists = api_version[1] + + if first_two_minor_digists == 11 and last_two_minor_digists < 8: + return True + elif first_two_minor_digists == 12 and last_two_minor_digists < 6: + return True + elif first_two_minor_digists == 13 and last_two_minor_digists < 4: + return True + elif first_two_minor_digists < 11: + return True + + return False + def execute(self): self.get_service_account_token() # From within a Pod we may have extra credentials - if self.access_api_server_version_end_point(): - self.publish_event(ServerApiVersionEndPointAccess(self.api_server_evidence)) + api_version = self.get_api_server_version_end_point() + + if api_version: + if self.check_cve_2018_1002105(api_version): + self.publish_event(ServerApiVersionEndPointAccessPE(self.api_server_evidence)) + + elif self.check_cve_2019_1002100(api_version): + self.publish_event(ServerApiVersionEndPointAccessDos(self.api_server_evidence)) + From 5935e0ba96c988764ff70f969aa46805147d02db Mon Sep 17 00:00:00 2001 From: Idan Revivo Date: Mon, 4 Mar 2019 11:33:39 +0200 Subject: [PATCH 03/14] changed checking all cves --- src/modules/hunting/cvehunter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/cvehunter.py b/src/modules/hunting/cvehunter.py index 1f035f1..e862c1f 100644 --- a/src/modules/hunting/cvehunter.py +++ b/src/modules/hunting/cvehunter.py @@ -115,7 +115,7 @@ class IsVulnerableToCVEAttack(Hunter): if self.check_cve_2018_1002105(api_version): self.publish_event(ServerApiVersionEndPointAccessPE(self.api_server_evidence)) - elif self.check_cve_2019_1002100(api_version): + if self.check_cve_2019_1002100(api_version): self.publish_event(ServerApiVersionEndPointAccessDos(self.api_server_evidence)) From 931e76f64de5fa790ab219f37821c9d2ab91cf00 Mon Sep 17 00:00:00 2001 From: Idan Revivo Date: Mon, 4 Mar 2019 13:48:20 +0200 Subject: [PATCH 04/14] changed cve details --- src/modules/hunting/cvehunter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/hunting/cvehunter.py b/src/modules/hunting/cvehunter.py index e862c1f..c9c4060 100644 --- a/src/modules/hunting/cvehunter.py +++ b/src/modules/hunting/cvehunter.py @@ -22,10 +22,10 @@ class ServerApiVersionEndPointAccessPE(Vulnerability, Event): class ServerApiVersionEndPointAccessDos(Vulnerability, Event): - """Node is vulnerable to critical CVE-2019-1002100""" + """Users that are authorized to make patch requests to the Kubernetes API Server can send a specially crafted patch of type json-patch that consumes excessive resources while processing, causing a Denial of Service on the API Server. CVE-2019-1002100""" def __init__(self, evidence): - Vulnerability.__init__(self, KubernetesCluster, name="Medium Denial of Service CVE", category=DenialOfService) + Vulnerability.__init__(self, KubernetesCluster, name="Denial of Service to Kubernetes API Server", category=DenialOfService) self.evidence = evidence From b7222d26e789c994f34c2bca96202d23ccf47775 Mon Sep 17 00:00:00 2001 From: Idan Revivo Date: Mon, 4 Mar 2019 17:05:17 +0200 Subject: [PATCH 05/14] cve info change --- src/modules/hunting/cvehunter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/cvehunter.py b/src/modules/hunting/cvehunter.py index c9c4060..1141067 100644 --- a/src/modules/hunting/cvehunter.py +++ b/src/modules/hunting/cvehunter.py @@ -22,7 +22,7 @@ class ServerApiVersionEndPointAccessPE(Vulnerability, Event): class ServerApiVersionEndPointAccessDos(Vulnerability, Event): - """Users that are authorized to make patch requests to the Kubernetes API Server can send a specially crafted patch of type json-patch that consumes excessive resources while processing, causing a Denial of Service on the API Server. CVE-2019-1002100""" + """Node not patched for CVE-2019-1002100. Depending on your RBAC settings, a crafted json-patch could cause a Denial of Service.""" def __init__(self, evidence): Vulnerability.__init__(self, KubernetesCluster, name="Denial of Service to Kubernetes API Server", category=DenialOfService) From 45d32be21279e0dd8ee450550ccbe13d5aa9dc63 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Sun, 24 Feb 2019 23:45:45 +0000 Subject: [PATCH 06/14] support for python3 Signed-off-by: Weston Steimel --- src/__init__.py | 4 ++-- src/core/__init__.py | 4 ++-- src/core/events/__init__.py | 4 ++-- src/core/events/handler.py | 2 +- src/core/events/types/__init__.py | 4 ++-- src/core/types.py | 2 +- src/modules/__init__.py | 6 +++--- src/modules/discovery/__init__.py | 3 ++- src/modules/hunting/__init__.py | 2 +- src/modules/hunting/aks.py | 2 +- src/modules/report/__init__.py | 2 +- src/modules/report/base.py | 2 +- src/modules/report/json_reporter.py | 2 +- src/modules/report/plain.py | 2 +- src/modules/report/yaml.py | 7 +++---- tests/discovery/test_hosts.py | 4 ++-- 16 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index d96bb8d..e34aa65 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,2 +1,2 @@ -import core -import modules \ No newline at end of file +from . import core +from . import modules diff --git a/src/core/__init__.py b/src/core/__init__.py index d516ffa..48f5bcf 100644 --- a/src/core/__init__.py +++ b/src/core/__init__.py @@ -1,2 +1,2 @@ -import types -import events \ No newline at end of file +from . import types +from . import events diff --git a/src/core/events/__init__.py b/src/core/events/__init__.py index 1dbbdff..5f76db6 100644 --- a/src/core/events/__init__.py +++ b/src/core/events/__init__.py @@ -1,2 +1,2 @@ -from handler import * -import types \ No newline at end of file +from .handler import * +from . import types diff --git a/src/core/events/handler.py b/src/core/events/handler.py index 652a257..0ca0e72 100644 --- a/src/core/events/handler.py +++ b/src/core/events/handler.py @@ -2,7 +2,7 @@ import logging import time from abc import ABCMeta from collections import defaultdict -from Queue import Queue +from queue import Queue from threading import Lock, Thread from __main__ import config diff --git a/src/core/events/types/__init__.py b/src/core/events/types/__init__.py index 7be8215..004dbd3 100644 --- a/src/core/events/types/__init__.py +++ b/src/core/events/types/__init__.py @@ -1,10 +1,10 @@ from os.path import dirname, basename, isfile import glob -from common import * +from .common import * # dynamically importing all modules in folder files = glob.glob(dirname(__file__)+"/*.py") for module_name in (basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')): if module_name != "handler": - exec('from {} import *'.format(module_name)) \ No newline at end of file + exec('from .{} import *'.format(module_name)) diff --git a/src/core/types.py b/src/core/types.py index 0e13e64..85e16f1 100644 --- a/src/core/types.py +++ b/src/core/types.py @@ -51,4 +51,4 @@ class PrivilegeEscalation(KubernetesCluster): class DenialOfService(object): name = "Denial of Service" -from events import handler # import is in the bottom to break import loops \ No newline at end of file +from .events import handler # import is in the bottom to break import loops diff --git a/src/modules/__init__.py b/src/modules/__init__.py index a35d22b..9586599 100644 --- a/src/modules/__init__.py +++ b/src/modules/__init__.py @@ -1,3 +1,3 @@ -import report -import discovery -import hunting \ No newline at end of file +from . import report +from . import discovery +from . import hunting diff --git a/src/modules/discovery/__init__.py b/src/modules/discovery/__init__.py index e1e1462..3a5ad78 100644 --- a/src/modules/discovery/__init__.py +++ b/src/modules/discovery/__init__.py @@ -4,4 +4,5 @@ import glob # dynamically importing all modules in folder files = glob.glob(dirname(__file__)+"/*.py") for module_name in (basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')): - exec('from {} import *'.format(module_name)) \ No newline at end of file + if not module_name.startswith('test_'): + exec('from .{} import *'.format(module_name)) diff --git a/src/modules/hunting/__init__.py b/src/modules/hunting/__init__.py index e1e1462..6c5952c 100644 --- a/src/modules/hunting/__init__.py +++ b/src/modules/hunting/__init__.py @@ -4,4 +4,4 @@ import glob # dynamically importing all modules in folder files = glob.glob(dirname(__file__)+"/*.py") for module_name in (basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')): - exec('from {} import *'.format(module_name)) \ No newline at end of file + exec('from .{} import *'.format(module_name)) diff --git a/src/modules/hunting/aks.py b/src/modules/hunting/aks.py index 6090295..527a2eb 100644 --- a/src/modules/hunting/aks.py +++ b/src/modules/hunting/aks.py @@ -3,7 +3,7 @@ import logging import requests -from kubelet import ExposedRunHandler +from .kubelet import ExposedRunHandler from ...core.events import handler from ...core.events.types import Event, Vulnerability diff --git a/src/modules/report/__init__.py b/src/modules/report/__init__.py index e1e1462..6c5952c 100644 --- a/src/modules/report/__init__.py +++ b/src/modules/report/__init__.py @@ -4,4 +4,4 @@ import glob # dynamically importing all modules in folder files = glob.glob(dirname(__file__)+"/*.py") for module_name in (basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')): - exec('from {} import *'.format(module_name)) \ No newline at end of file + exec('from .{} import *'.format(module_name)) diff --git a/src/modules/report/base.py b/src/modules/report/base.py index b8986a0..36dda55 100644 --- a/src/modules/report/base.py +++ b/src/modules/report/base.py @@ -1,4 +1,4 @@ -from collector import services, vulnerabilities, services_lock, vulnerabilities_lock +from .collector import services, vulnerabilities, services_lock, vulnerabilities_lock class BaseReporter(object): def get_nodes(self): diff --git a/src/modules/report/json_reporter.py b/src/modules/report/json_reporter.py index 356d7c9..0917188 100644 --- a/src/modules/report/json_reporter.py +++ b/src/modules/report/json_reporter.py @@ -1,5 +1,5 @@ import json -from base import BaseReporter +from .base import BaseReporter class JSONReporter(BaseReporter): def get_report(self): diff --git a/src/modules/report/plain.py b/src/modules/report/plain.py index 4171f33..282035a 100644 --- a/src/modules/report/plain.py +++ b/src/modules/report/plain.py @@ -3,7 +3,7 @@ from __future__ import print_function from prettytable import ALL, PrettyTable from __main__ import config -from collector import services, vulnerabilities, services_lock, vulnerabilities_lock +from .collector import services, vulnerabilities, services_lock, vulnerabilities_lock EVIDENCE_PREVIEW = 40 MAX_TABLE_WIDTH = 20 diff --git a/src/modules/report/yaml.py b/src/modules/report/yaml.py index 2060fed..07abd94 100644 --- a/src/modules/report/yaml.py +++ b/src/modules/report/yaml.py @@ -1,7 +1,6 @@ -import StringIO - +from io import StringIO from ruamel.yaml import YAML -from base import BaseReporter +from .base import BaseReporter class YAMLReporter(BaseReporter): def get_report(self): @@ -13,4 +12,4 @@ class YAMLReporter(BaseReporter): } output = StringIO.StringIO() yaml.dump(report, output) - return output.getvalue() \ No newline at end of file + return output.getvalue() diff --git a/tests/discovery/test_hosts.py b/tests/discovery/test_hosts.py index 57b3ad4..d136ef9 100644 --- a/tests/discovery/test_hosts.py +++ b/tests/discovery/test_hosts.py @@ -1,6 +1,6 @@ import requests_mock import time -from Queue import Empty +from queue import Empty from src.modules.discovery.hosts import FromPodHostDiscovery, RunningAsPodEvent, HostScanEvent, AzureMetadataApi from src.core.events.types import Event, NewHostEvent @@ -59,4 +59,4 @@ class testHostDiscoveryEvent(object): @handler.subscribe(AzureMetadataApi) class testAzureMetadataApi(object): def __init__(self, event): - assert config.azure \ No newline at end of file + assert config.azure From 71f52c0d2c238b22afbd231f0d257610a1498a7b Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 25 Feb 2019 23:14:01 +0000 Subject: [PATCH 07/14] add future as requirement Signed-off-by: Weston Steimel --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 5cc2a79..20c49a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ PrettyTable urllib3 ruamel.yaml requests_mock +future From c44d6874dc21bdbdb55e3c368dc5ef0414b19f51 Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Tue, 5 Mar 2019 12:30:22 +0000 Subject: [PATCH 08/14] Update travis for python 3 as well as python 2 Now that we have #95 --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 39358c7..6d9f699 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,6 @@ group: travis_latest language: python cache: pip matrix: - allow_failures: - # Python 3 tests are failing due to an import error - - python: 3.6 include: - python: 2.7 #- python: 3.4 From d66180d7cc1a4f38957140bbee8795114d6e9e3f Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Tue, 5 Mar 2019 12:33:16 +0000 Subject: [PATCH 09/14] Add build status badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 864e303..3211850 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ![kube-hunter](https://github.com/aquasecurity/kube-hunter/blob/master/kube-hunter.png) +[![Build Status](https://travis-ci.org/aquasecurity/kube-hunter.svg?branch=master)](https://travis-ci.org/aquasecurity/kube-hunter) + 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 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 see and share the results online. You can also run the Python code yourself as described below. From f2b3573beec6f3f7152fe730c2f321e4f8719cde Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Wed, 6 Mar 2019 20:36:43 +0000 Subject: [PATCH 10/14] Add python 3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3211850..b5b5c40 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ You can run the kube-hunter python code directly on your machine. #### Prerequisites You will need the following installed: -* python 2.7 +* python 2.7 or python 3.x * pip Clone the repository: From 1cd44832e60ba94bd02bd5225c93c5c87c14a735 Mon Sep 17 00:00:00 2001 From: Michael Cherny Date: Thu, 7 Mar 2019 14:45:26 +0200 Subject: [PATCH 11/14] Fixes #99 - pod local vulnerabilities are now reported as "Local to Pod" ( ) Event can now implement 'location()' method that return string representing events logical location. In events chain, the 'newest' event available location method will be used. This is because we compose (chain) events. Core changed to support it. Added 'location()' method to relevant event classes. Reports are now using vulnerability.location() to retrieve location. --- src/core/events/types/common.py | 25 +++++++++++++++++++++++-- src/modules/discovery/hosts.py | 8 ++++++++ src/modules/report/base.py | 2 +- src/modules/report/plain.py | 2 +- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/core/events/types/common.py b/src/core/events/types/common.py index ffe60a4..2ec8acf 100644 --- a/src/core/events/types/common.py +++ b/src/core/events/types/common.py @@ -16,6 +16,17 @@ class Event(object): if name in event.__dict__: return event.__dict__[name] + # 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 + def location(self): + location = None + if self.previous: + location = self.previous.location() + + return location + # returns the event history ordered from newest to oldest @property def history(self): @@ -85,7 +96,10 @@ class NewHostEvent(Event): def __str__(self): return str(self.host) - + + # Event's logical location to be used mainly for reports. + def location(self): + return str(self.host) class OpenPortEvent(Event): def __init__(self, port): @@ -93,7 +107,14 @@ class OpenPortEvent(Event): def __str__(self): return str(self.port) - + + # Event's logical location to be used mainly for reports. + def location(self): + if self.host: + location = str(self.host) + ":" + str(self.port) + else: + location = str(self.port) + return location class HuntFinished(Event): pass diff --git a/src/modules/discovery/hosts.py b/src/modules/discovery/hosts.py index b942ce3..afce667 100644 --- a/src/modules/discovery/hosts.py +++ b/src/modules/discovery/hosts.py @@ -22,6 +22,14 @@ class RunningAsPodEvent(Event): self.auth_token = self.get_auth_token() self.client_cert = self.get_client_cert() + # Event's logical location to be used mainly for reports. + def location(self): + location = "Local to Pod" + if 'HOSTNAME' in os.environ: + location += "(" + os.environ['HOSTNAME'] + ")" + + return location + def get_auth_token(self): try: with open("/var/run/secrets/kubernetes.io/serviceaccount/token") as token_file: diff --git a/src/modules/report/base.py b/src/modules/report/base.py index 36dda55..786cf26 100644 --- a/src/modules/report/base.py +++ b/src/modules/report/base.py @@ -24,7 +24,7 @@ class BaseReporter(object): def get_vulnerabilities(self): vulnerabilities_lock.acquire() - vulnerabilities_data = [{"location": "{}:{}".format(vuln.host, vuln.port) if vuln.host else "", + vulnerabilities_data = [{"location": vuln.location(), "category": vuln.category.name, "vulnerability": vuln.get_name(), "description": vuln.explain(), diff --git a/src/modules/report/plain.py b/src/modules/report/plain.py index 282035a..70b88eb 100644 --- a/src/modules/report/plain.py +++ b/src/modules/report/plain.py @@ -82,7 +82,7 @@ class PlainReporter(object): vulnerabilities_lock.acquire() for vuln in vulnerabilities: - row = ["{}:{}".format(vuln.host, vuln.port) if vuln.host else "", vuln.category.name, vuln.get_name(), vuln.explain()] + row = [vuln.location(), vuln.category.name, vuln.get_name(), vuln.explain()] evidence = str(vuln.evidence)[:EVIDENCE_PREVIEW] + "..." if len(str(vuln.evidence)) > EVIDENCE_PREVIEW else str(vuln.evidence) row.append(evidence) vuln_table.add_row(row) From 0c0a68883db09e377cc565bf66608d6c437c3a60 Mon Sep 17 00:00:00 2001 From: Michael Cherny Date: Thu, 7 Mar 2019 20:44:56 +0200 Subject: [PATCH 12/14] Fix #98 - cvehunter now using service token discovered in hosts.py We use the token if available. --- src/modules/hunting/cvehunter.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/modules/hunting/cvehunter.py b/src/modules/hunting/cvehunter.py index 1141067..daf5a6f 100644 --- a/src/modules/hunting/cvehunter.py +++ b/src/modules/hunting/cvehunter.py @@ -37,26 +37,20 @@ class IsVulnerableToCVEAttack(Hunter): def __init__(self, event): self.event = event self.headers = dict() + # From within a Pod we may have extra credentials + if self.event.auth_token: + self.headers = {'Authorization': 'Bearer ' + self.event.auth_token} self.path = "https://{}:{}".format(self.event.host, self.event.port) self.service_account_token_evidence = '' self.api_server_evidence = '' self.k8sVersion = '' - def get_service_account_token(self): - logging.debug(self.event.host) - logging.debug('Passive Hunter is attempting to access pod\'s service account token') - try: - with open('/var/run/secrets/kubernetes.io/serviceaccount/token', 'r') as token: - data = token.read() - self.service_account_token_evidence = data - self.headers = {'Authorization': 'Bearer ' + self.service_account_token_evidence} - return True - except IOError: # Couldn't read file - return False - def get_api_server_version_end_point(self): logging.debug(self.event.host) - logging.debug('Passive Hunter is attempting to access the API server version end point using the pod\'s service account token') + if 'Authorization' in self.headers: + logging.debug('Passive Hunter is attempting to access the API server version end point using the pod\'s service account token: \t%s', str(self.headers)) + else: + logging.debug('Passive Hunter is attempting to access the API server version end point anonymously') try: res = requests.get("{path}/version".format(path=self.path), headers=self.headers, verify=False) @@ -65,6 +59,7 @@ class IsVulnerableToCVEAttack(Hunter): version = resDict["gitVersion"].split('.') first_two_minor_digits = eval(version[1]) last_two_minor_digits = eval(version[2]) + logging.debug('Passive Hunter got version from the API server version end point: %d.%d', first_two_minor_digits, last_two_minor_digits) return [first_two_minor_digits, last_two_minor_digits] except (requests.exceptions.ConnectionError, KeyError): @@ -108,7 +103,6 @@ class IsVulnerableToCVEAttack(Hunter): return False def execute(self): - self.get_service_account_token() # From within a Pod we may have extra credentials api_version = self.get_api_server_version_end_point() if api_version: From 22334c67ad2328995e03635ccfdb3ff8dc131fbb Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Thu, 7 Mar 2019 17:57:09 +0000 Subject: [PATCH 13/14] update dockerfile and travis * Update docker image to python 3.7.2 and alpine3.9 * Update travis to test python3.7 * Remove part about using python2 in python3-based environment from README Signed-off-by: Weston Steimel --- .travis.yml | 4 ++-- Dockerfile | 2 +- README.md | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6d9f699..1499316 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,8 @@ matrix: #- python: 3.4 #- python: 3.5 - python: 3.6 - #- python: 3.7 - # dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) + - python: 3.7 + dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) # sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069) install: - pip install -r requirements.txt diff --git a/Dockerfile b/Dockerfile index 8bbc3fc..e0f3ccb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:2.7.15-alpine3.8 +FROM python:3.7.2-alpine3.9 RUN apk add --update \ linux-headers \ diff --git a/README.md b/README.md index b5b5c40..73f93df 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,8 @@ Install module dependencies: ~~~ cd ./kube-hunter pip install -r requirements.txt - -In the case where you have python 3.x in the path as your default, and python2 refers to a python 2.7 executable, use "python2 -m pip install -r requirements.txt" ~~~ + Run: `./kube-hunter.py` From c59b199a2401a3eccf71efac469b6396c28aa726 Mon Sep 17 00:00:00 2001 From: Michael Cherny Date: Mon, 11 Mar 2019 00:56:24 +0530 Subject: [PATCH 14/14] Removed unused variable --- src/modules/hunting/cvehunter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/hunting/cvehunter.py b/src/modules/hunting/cvehunter.py index daf5a6f..a3f7c34 100644 --- a/src/modules/hunting/cvehunter.py +++ b/src/modules/hunting/cvehunter.py @@ -41,7 +41,6 @@ class IsVulnerableToCVEAttack(Hunter): if self.event.auth_token: self.headers = {'Authorization': 'Bearer ' + self.event.auth_token} self.path = "https://{}:{}".format(self.event.host, self.event.port) - self.service_account_token_evidence = '' self.api_server_evidence = '' self.k8sVersion = ''