From 105bc393da0abf9dfa7dbdc8441ab8395bce51a9 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Tue, 9 Oct 2018 21:51:49 +0300 Subject: [PATCH 01/28] access to secrets from within the pod hunter --- src/modules/hunting/secrets.py | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/modules/hunting/secrets.py diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py new file mode 100644 index 0000000..a58d713 --- /dev/null +++ b/src/modules/hunting/secrets.py @@ -0,0 +1,47 @@ +import json +import logging +import os + + +import requests + +from ...core.events import handler +from ...core.events.types import Vulnerability, Event, OpenPortEvent +from ...core.types import Hunter, KubernetesCluster, AccessRisk + + +""" Vulnerabilities """ +class secretsAccess(Vulnerability, Event): + """ Accessing the server API within a compromised pod would help an attacker gain full control over the cluster""" + + def __init__(self, evidence): + Vulnerability.__init__(self, KubernetesCluster, name="Accessed to pod's secrets", category=AccessRisk) + self.evidence = evidence + +# Passive Hunter +#should change the subscribtion here... (openPortEvent isnt relevant..) +@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443 or p.port == 443 or p.port == 10250 or p.port == 10255 or p.port == 2379) +class AccessSecrets(Hunter): + """Accessing the secrets accessible to the pod""" + + def __init__(self, event): + self.event = event + self.secrets_evidence = '' + + def get_services(self): + logging.debug(self.event.host) + self.secrets_evidence = os.listdir('/var/run/secrets') + if len(self.secrets_evidence) > 0: + return True + return False + + #todo: + # remove traceback + def execute(self): + try: + if self.get_services(): + self.publish_event(secretsAccess(self.secrets_evidence)) + + except: + import traceback + traceback.print_exc() From 18cbe6c53e25bc96253b558e2a77559936d3a48b Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Tue, 9 Oct 2018 22:15:28 +0300 Subject: [PATCH 02/28] changed the way im checking how many secrets there are at the default secrets path --- src/modules/hunting/secrets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index a58d713..3f25efe 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -30,7 +30,8 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) - self.secrets_evidence = os.listdir('/var/run/secrets') + # get all files and subdirectories files: + self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('./')] for val in sublist] if len(self.secrets_evidence) > 0: return True return False From 5efe38145147f12bf128d81259db3d9e24eb1f42 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Tue, 9 Oct 2018 22:21:24 +0300 Subject: [PATCH 03/28] changed the way im checking how many secrets there are at the default secrets path --- src/modules/hunting/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 3f25efe..19ee0c0 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -31,7 +31,7 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) # get all files and subdirectories files: - self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('./')] for val in sublist] + self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] if len(self.secrets_evidence) > 0: return True return False From bb01d1bad91ba88a52f9b7f0b333b3c3a05e2a1e Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Wed, 10 Oct 2018 11:23:49 +0300 Subject: [PATCH 04/28] removed the traceback after tested successfully --- src/modules/hunting/secrets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 19ee0c0..e92d308 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -36,13 +36,10 @@ class AccessSecrets(Hunter): return True return False - #todo: - # remove traceback def execute(self): try: if self.get_services(): self.publish_event(secretsAccess(self.secrets_evidence)) except: - import traceback - traceback.print_exc() + pass From c01de32f043fcfa99b0fec2423e2c2039ace27cb Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Wed, 10 Oct 2018 11:29:44 +0300 Subject: [PATCH 05/28] Improved description for this hunter --- src/modules/hunting/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index e92d308..101994c 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -12,7 +12,7 @@ from ...core.types import Hunter, KubernetesCluster, AccessRisk """ Vulnerabilities """ class secretsAccess(Vulnerability, Event): - """ Accessing the server API within a compromised pod would help an attacker gain full control over the cluster""" + """ Accessing the pod's secrets within a compromised pod might disclose valuable data to a potential attacker""" def __init__(self, evidence): Vulnerability.__init__(self, KubernetesCluster, name="Accessed to pod's secrets", category=AccessRisk) From 29a004239bc4e0af0e0e8202f5bb473a6012c9f2 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Wed, 10 Oct 2018 18:04:08 +0300 Subject: [PATCH 06/28] changed subscription --- src/modules/hunting/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 101994c..2c934d8 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -20,7 +20,7 @@ class secretsAccess(Vulnerability, Event): # Passive Hunter #should change the subscribtion here... (openPortEvent isnt relevant..) -@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443 or p.port == 443 or p.port == 10250 or p.port == 10255 or p.port == 2379) +@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443) class AccessSecrets(Hunter): """Accessing the secrets accessible to the pod""" From d401ede6368c1298940c2b9eb2f56bf382eed8e4 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 11:50:11 +0300 Subject: [PATCH 07/28] Removed try & except & added logging --- src/modules/hunting/secrets.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 2c934d8..a68c1ad 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -30,6 +30,7 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) + logging.debug('Passive Hunter is attempting to access pod\'s secrets directory') # get all files and subdirectories files: self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] if len(self.secrets_evidence) > 0: @@ -37,9 +38,5 @@ class AccessSecrets(Hunter): return False def execute(self): - try: - if self.get_services(): - self.publish_event(secretsAccess(self.secrets_evidence)) - - except: - pass + if self.get_services(): + self.publish_event(secretsAccess(self.secrets_evidence)) From 0c6de23c65609ea001f695337c09e8a0f8a25241 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 12:00:13 +0300 Subject: [PATCH 08/28] Fixed all PR comments, just have to change the subscription now.. --- src/modules/hunting/secrets.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index a68c1ad..7654d0d 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -11,7 +11,7 @@ from ...core.types import Hunter, KubernetesCluster, AccessRisk """ Vulnerabilities """ -class secretsAccess(Vulnerability, Event): +class SecretsAccess(Vulnerability, Event): """ Accessing the pod's secrets within a compromised pod might disclose valuable data to a potential attacker""" def __init__(self, evidence): @@ -33,10 +33,8 @@ class AccessSecrets(Hunter): logging.debug('Passive Hunter is attempting to access pod\'s secrets directory') # get all files and subdirectories files: self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] - if len(self.secrets_evidence) > 0: - return True - return False + return True if len(self.secrets_evidence) > 0 else False def execute(self): if self.get_services(): - self.publish_event(secretsAccess(self.secrets_evidence)) + self.publish_event(SecretsAccess(self.secrets_evidence)) From bff5ce75587529d296e8f11070dc97a5964287e4 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 14:51:15 +0300 Subject: [PATCH 09/28] Had to remove the Azure component form the hunting/aks since it made a circular dependency bug! --- src/core/types.py | 3 +++ src/modules/discovery/hosts.py | 3 +-- src/modules/hunting/aks.py | 5 +---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/types.py b/src/core/types.py index ac3d385..626ebc9 100644 --- a/src/core/types.py +++ b/src/core/types.py @@ -17,6 +17,9 @@ class Kubelet(KubernetesCluster): """The kubelet is the primary "node agent" that runs on each node""" name = "Kubelet" +class Azure(KubernetesCluster): + """Azure Cluster""" + name = "Azure" """ Categories """ class InformationDisclosure(object): diff --git a/src/modules/discovery/hosts.py b/src/modules/discovery/hosts.py index e255407..fcde41f 100644 --- a/src/modules/discovery/hosts.py +++ b/src/modules/discovery/hosts.py @@ -13,8 +13,7 @@ from netifaces import AF_INET, ifaddresses, interfaces from ...core.events import handler from ...core.events.types import Event, NewHostEvent, Vulnerability -from ...core.types import Hunter, InformationDisclosure -from ..hunting.aks import Azure +from ...core.types import Hunter, InformationDisclosure, Azure class AzureMetadataApi(Vulnerability, Event): diff --git a/src/modules/hunting/aks.py b/src/modules/hunting/aks.py index 3275fb6..2fdaf0c 100644 --- a/src/modules/hunting/aks.py +++ b/src/modules/hunting/aks.py @@ -8,10 +8,7 @@ from kubelet import ExposedRunHandler from ...core.events import handler from ...core.events.types import Event, Vulnerability from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft - -class Azure(KubernetesCluster): - """Azure Cluster""" - name = "Azure" + class AzureSpnExposure(Vulnerability, Event): """The SPN is exposed, potentially allowing an attacker to gain access to the Azure subscription""" From 4b466f61c3039451645663ae0ac056ad246f50fd Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 15:09:39 +0300 Subject: [PATCH 10/28] Created RunningAsPodEvent Throw it from hosts.py when running form pod I was able to subscribe to the RunningAsPodEvent thanks to a Rebased with the branch that fix the circular dependencies bug (moveAzureComponentToTypes branch) --- src/modules/discovery/hosts.py | 6 ++++++ src/modules/hunting/secrets.py | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/modules/discovery/hosts.py b/src/modules/discovery/hosts.py index fcde41f..4f837cc 100644 --- a/src/modules/discovery/hosts.py +++ b/src/modules/discovery/hosts.py @@ -16,6 +16,11 @@ from ...core.events.types import Event, NewHostEvent, Vulnerability from ...core.types import Hunter, InformationDisclosure, Azure +class RunningAsPodEvent(Event): + def __init__(self): + self.name = 'Running from within a pod' + + class AzureMetadataApi(Vulnerability, Event): """Access to the Azure Metadata API exposes sensitive information about the machines associated with the cluster""" def __init__(self, cidr): @@ -65,6 +70,7 @@ class HostDiscovery(Hunter): for host in config.remote: self.publish_event(NewHostEvent(host=host, cloud=self.get_cloud(host))) elif config.pod: + self.publish_event(RunningAsPodEvent()) if self.is_azure_pod(): self.azure_metadata_discovery() else: diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 7654d0d..9667a32 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -6,9 +6,9 @@ import os import requests from ...core.events import handler -from ...core.events.types import Vulnerability, Event, OpenPortEvent -from ...core.types import Hunter, KubernetesCluster, AccessRisk - +from ...core.events.types import Vulnerability, Event +from ...core.types import Hunter, KubernetesCluster, AccessRisk +from ..discovery.hosts import RunningAsPodEvent """ Vulnerabilities """ class SecretsAccess(Vulnerability, Event): @@ -18,9 +18,9 @@ class SecretsAccess(Vulnerability, Event): Vulnerability.__init__(self, KubernetesCluster, name="Accessed to pod's secrets", category=AccessRisk) self.evidence = evidence + # Passive Hunter -#should change the subscribtion here... (openPortEvent isnt relevant..) -@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443) +@handler.subscribe(RunningAsPodEvent) class AccessSecrets(Hunter): """Accessing the secrets accessible to the pod""" From 493d7d6d38d4cb9b4e818445edcb3e597a92d115 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 15:24:11 +0300 Subject: [PATCH 11/28] Created RunningAsPodEvent Throw it from hosts.py when running form pod I was able to subscribe to the RunningAsPodEvent thanks to a Rebased with the branch that fix the circular dependencies bug (moveAzureComponentToTypes branch) --- src/modules/hunting/aks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/aks.py b/src/modules/hunting/aks.py index 2fdaf0c..16257e2 100644 --- a/src/modules/hunting/aks.py +++ b/src/modules/hunting/aks.py @@ -7,7 +7,7 @@ from kubelet import ExposedRunHandler from ...core.events import handler from ...core.events.types import Event, Vulnerability -from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft +from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft, Azure class AzureSpnExposure(Vulnerability, Event): From e66f427f928be11e5cabc463625b63e16f91559c Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Tue, 9 Oct 2018 21:51:49 +0300 Subject: [PATCH 12/28] access to secrets from within the pod hunter --- src/modules/hunting/secrets.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 9667a32..a58d713 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -6,21 +6,21 @@ import os import requests from ...core.events import handler -from ...core.events.types import Vulnerability, Event -from ...core.types import Hunter, KubernetesCluster, AccessRisk -from ..discovery.hosts import RunningAsPodEvent +from ...core.events.types import Vulnerability, Event, OpenPortEvent +from ...core.types import Hunter, KubernetesCluster, AccessRisk + """ Vulnerabilities """ -class SecretsAccess(Vulnerability, Event): - """ Accessing the pod's secrets within a compromised pod might disclose valuable data to a potential attacker""" +class secretsAccess(Vulnerability, Event): + """ Accessing the server API within a compromised pod would help an attacker gain full control over the cluster""" def __init__(self, evidence): Vulnerability.__init__(self, KubernetesCluster, name="Accessed to pod's secrets", category=AccessRisk) self.evidence = evidence - # Passive Hunter -@handler.subscribe(RunningAsPodEvent) +#should change the subscribtion here... (openPortEvent isnt relevant..) +@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443 or p.port == 443 or p.port == 10250 or p.port == 10255 or p.port == 2379) class AccessSecrets(Hunter): """Accessing the secrets accessible to the pod""" @@ -30,11 +30,18 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) - logging.debug('Passive Hunter is attempting to access pod\'s secrets directory') - # get all files and subdirectories files: - self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] - return True if len(self.secrets_evidence) > 0 else False + self.secrets_evidence = os.listdir('/var/run/secrets') + if len(self.secrets_evidence) > 0: + return True + return False + #todo: + # remove traceback def execute(self): - if self.get_services(): - self.publish_event(SecretsAccess(self.secrets_evidence)) + try: + if self.get_services(): + self.publish_event(secretsAccess(self.secrets_evidence)) + + except: + import traceback + traceback.print_exc() From c7b1874dbb3c4ce1dcbd8704761ac7e9127e36d9 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Tue, 9 Oct 2018 22:15:28 +0300 Subject: [PATCH 13/28] changed the way im checking how many secrets there are at the default secrets path --- src/modules/hunting/secrets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index a58d713..3f25efe 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -30,7 +30,8 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) - self.secrets_evidence = os.listdir('/var/run/secrets') + # get all files and subdirectories files: + self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('./')] for val in sublist] if len(self.secrets_evidence) > 0: return True return False From 5de247b0f54f9f7a96102ca4486a7a8193ee6bc6 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Tue, 9 Oct 2018 22:21:24 +0300 Subject: [PATCH 14/28] changed the way im checking how many secrets there are at the default secrets path --- src/modules/hunting/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 3f25efe..19ee0c0 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -31,7 +31,7 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) # get all files and subdirectories files: - self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('./')] for val in sublist] + self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] if len(self.secrets_evidence) > 0: return True return False From d3658f2d3d646c6f10f138549603def7588ad9b5 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Wed, 10 Oct 2018 11:23:49 +0300 Subject: [PATCH 15/28] removed the traceback after tested successfully --- src/modules/hunting/secrets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 19ee0c0..e92d308 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -36,13 +36,10 @@ class AccessSecrets(Hunter): return True return False - #todo: - # remove traceback def execute(self): try: if self.get_services(): self.publish_event(secretsAccess(self.secrets_evidence)) except: - import traceback - traceback.print_exc() + pass From efd2563e2a3ccedec26575b2396d07a038de6c25 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Wed, 10 Oct 2018 11:29:44 +0300 Subject: [PATCH 16/28] Improved description for this hunter --- src/modules/hunting/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index e92d308..101994c 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -12,7 +12,7 @@ from ...core.types import Hunter, KubernetesCluster, AccessRisk """ Vulnerabilities """ class secretsAccess(Vulnerability, Event): - """ Accessing the server API within a compromised pod would help an attacker gain full control over the cluster""" + """ Accessing the pod's secrets within a compromised pod might disclose valuable data to a potential attacker""" def __init__(self, evidence): Vulnerability.__init__(self, KubernetesCluster, name="Accessed to pod's secrets", category=AccessRisk) From 8a8f2272ec309a55bd5bddaaf4fefc375bb1bc51 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Wed, 10 Oct 2018 18:04:08 +0300 Subject: [PATCH 17/28] changed subscription --- src/modules/hunting/secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 101994c..2c934d8 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -20,7 +20,7 @@ class secretsAccess(Vulnerability, Event): # Passive Hunter #should change the subscribtion here... (openPortEvent isnt relevant..) -@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443 or p.port == 443 or p.port == 10250 or p.port == 10255 or p.port == 2379) +@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443) class AccessSecrets(Hunter): """Accessing the secrets accessible to the pod""" From eb5a0a6df29232b8d2b21e274426d4aec913f9a7 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 11:50:11 +0300 Subject: [PATCH 18/28] Removed try & except & added logging --- src/modules/hunting/secrets.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 2c934d8..a68c1ad 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -30,6 +30,7 @@ class AccessSecrets(Hunter): def get_services(self): logging.debug(self.event.host) + logging.debug('Passive Hunter is attempting to access pod\'s secrets directory') # get all files and subdirectories files: self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] if len(self.secrets_evidence) > 0: @@ -37,9 +38,5 @@ class AccessSecrets(Hunter): return False def execute(self): - try: - if self.get_services(): - self.publish_event(secretsAccess(self.secrets_evidence)) - - except: - pass + if self.get_services(): + self.publish_event(secretsAccess(self.secrets_evidence)) From 1f9b6112377ec26faedd74951334b0cd19d4feb1 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 12:00:13 +0300 Subject: [PATCH 19/28] Fixed all PR comments, just have to change the subscription now.. --- src/modules/hunting/secrets.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index a68c1ad..7654d0d 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -11,7 +11,7 @@ from ...core.types import Hunter, KubernetesCluster, AccessRisk """ Vulnerabilities """ -class secretsAccess(Vulnerability, Event): +class SecretsAccess(Vulnerability, Event): """ Accessing the pod's secrets within a compromised pod might disclose valuable data to a potential attacker""" def __init__(self, evidence): @@ -33,10 +33,8 @@ class AccessSecrets(Hunter): logging.debug('Passive Hunter is attempting to access pod\'s secrets directory') # get all files and subdirectories files: self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] - if len(self.secrets_evidence) > 0: - return True - return False + return True if len(self.secrets_evidence) > 0 else False def execute(self): if self.get_services(): - self.publish_event(secretsAccess(self.secrets_evidence)) + self.publish_event(SecretsAccess(self.secrets_evidence)) From 08f38c623f7a9d138ed54079471567b9abb294f4 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 14:51:15 +0300 Subject: [PATCH 20/28] Had to remove the Azure component form the hunting/aks since it made a circular dependency bug! --- src/modules/discovery/hosts.py | 6 ------ src/modules/hunting/aks.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/modules/discovery/hosts.py b/src/modules/discovery/hosts.py index 4f837cc..fcde41f 100644 --- a/src/modules/discovery/hosts.py +++ b/src/modules/discovery/hosts.py @@ -16,11 +16,6 @@ from ...core.events.types import Event, NewHostEvent, Vulnerability from ...core.types import Hunter, InformationDisclosure, Azure -class RunningAsPodEvent(Event): - def __init__(self): - self.name = 'Running from within a pod' - - class AzureMetadataApi(Vulnerability, Event): """Access to the Azure Metadata API exposes sensitive information about the machines associated with the cluster""" def __init__(self, cidr): @@ -70,7 +65,6 @@ class HostDiscovery(Hunter): for host in config.remote: self.publish_event(NewHostEvent(host=host, cloud=self.get_cloud(host))) elif config.pod: - self.publish_event(RunningAsPodEvent()) if self.is_azure_pod(): self.azure_metadata_discovery() else: diff --git a/src/modules/hunting/aks.py b/src/modules/hunting/aks.py index 16257e2..2fdaf0c 100644 --- a/src/modules/hunting/aks.py +++ b/src/modules/hunting/aks.py @@ -7,7 +7,7 @@ from kubelet import ExposedRunHandler from ...core.events import handler from ...core.events.types import Event, Vulnerability -from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft, Azure +from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft class AzureSpnExposure(Vulnerability, Event): From 1f01076cf6a1cb89318ccf25e778db5245c115b1 Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 15:09:39 +0300 Subject: [PATCH 21/28] Created RunningAsPodEvent Throw it from hosts.py when running form pod I was able to subscribe to the RunningAsPodEvent thanks to a Rebased with the branch that fix the circular dependencies bug (moveAzureComponentToTypes branch) --- src/modules/discovery/hosts.py | 6 ++++++ src/modules/hunting/secrets.py | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/modules/discovery/hosts.py b/src/modules/discovery/hosts.py index fcde41f..4f837cc 100644 --- a/src/modules/discovery/hosts.py +++ b/src/modules/discovery/hosts.py @@ -16,6 +16,11 @@ from ...core.events.types import Event, NewHostEvent, Vulnerability from ...core.types import Hunter, InformationDisclosure, Azure +class RunningAsPodEvent(Event): + def __init__(self): + self.name = 'Running from within a pod' + + class AzureMetadataApi(Vulnerability, Event): """Access to the Azure Metadata API exposes sensitive information about the machines associated with the cluster""" def __init__(self, cidr): @@ -65,6 +70,7 @@ class HostDiscovery(Hunter): for host in config.remote: self.publish_event(NewHostEvent(host=host, cloud=self.get_cloud(host))) elif config.pod: + self.publish_event(RunningAsPodEvent()) if self.is_azure_pod(): self.azure_metadata_discovery() else: diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 7654d0d..9667a32 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -6,9 +6,9 @@ import os import requests from ...core.events import handler -from ...core.events.types import Vulnerability, Event, OpenPortEvent -from ...core.types import Hunter, KubernetesCluster, AccessRisk - +from ...core.events.types import Vulnerability, Event +from ...core.types import Hunter, KubernetesCluster, AccessRisk +from ..discovery.hosts import RunningAsPodEvent """ Vulnerabilities """ class SecretsAccess(Vulnerability, Event): @@ -18,9 +18,9 @@ class SecretsAccess(Vulnerability, Event): Vulnerability.__init__(self, KubernetesCluster, name="Accessed to pod's secrets", category=AccessRisk) self.evidence = evidence + # Passive Hunter -#should change the subscribtion here... (openPortEvent isnt relevant..) -@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 6443) +@handler.subscribe(RunningAsPodEvent) class AccessSecrets(Hunter): """Accessing the secrets accessible to the pod""" From 1e4ead93f471806e7396b7e515fc896af57b0ebd Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 15:24:11 +0300 Subject: [PATCH 22/28] Created RunningAsPodEvent Throw it from hosts.py when running form pod I was able to subscribe to the RunningAsPodEvent thanks to a Rebased with the branch that fix the circular dependencies bug (moveAzureComponentToTypes branch) --- src/modules/hunting/aks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hunting/aks.py b/src/modules/hunting/aks.py index 2fdaf0c..16257e2 100644 --- a/src/modules/hunting/aks.py +++ b/src/modules/hunting/aks.py @@ -7,7 +7,7 @@ from kubelet import ExposedRunHandler from ...core.events import handler from ...core.events.types import Event, Vulnerability -from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft +from ...core.types import Hunter, ActiveHunter, KubernetesCluster, IdentityTheft, Azure class AzureSpnExposure(Vulnerability, Event): From 1883abaa236faaba28fa2db2aa68d6cc7b6c289a Mon Sep 17 00:00:00 2001 From: "ori.agmon" Date: Sun, 14 Oct 2018 16:04:37 +0300 Subject: [PATCH 23/28] Updated read me for devs so the mistake won't happen again --- src/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/README.md b/src/README.md index 80f3612..868eb65 100644 --- a/src/README.md +++ b/src/README.md @@ -71,7 +71,10 @@ _The file's (module's) content is imported automatically"_ The second step is to determine what events your Hunter will subscribe to, and from where you can get them. `Convention:` Events should be declared in their corresponding module. for example, a KubeDashboardEvent event is declared in the dashboard discovery module. - + +`Note:` An hunter located under the `disovery` folder should not import any modules located under the `hunting` folder +in order to prevent circular dependency bugs + Following the above example, let's figure out the imports: ```python from ...core.types import Hunter @@ -114,7 +117,7 @@ relative import: `...core.types` ## Creating Events -As discussed above, we know there are alot of different types of events that can be created. but at the end, they all need to inherit from the base class `Event` +As discussed above, we know there are a lot of different types of events that can be created. but at the end, they all need to inherit from the base class `Event` lets see some examples of creating different types of events: ### Vulnerability ```python From 568e96c2f45f270590e0ca7b4d565322587aeceb Mon Sep 17 00:00:00 2001 From: oriagmon Date: Tue, 16 Oct 2018 17:18:36 +0300 Subject: [PATCH 24/28] merged with multi-threaded-bug --- src/modules/discovery/apiserver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/discovery/apiserver.py b/src/modules/discovery/apiserver.py index ef7c495..b3ec74c 100644 --- a/src/modules/discovery/apiserver.py +++ b/src/modules/discovery/apiserver.py @@ -16,6 +16,7 @@ class ApiServer(Service, Event): def __init__(self): Service.__init__(self, name="API Server") + @handler.subscribe(OpenPortEvent, predicate=lambda x: x.port==443 or x.port==6443) class ApiServerDiscovery(Hunter): """Api Server Discovery From e501e9ee6300a43a3d50b840ea7cec3a76552fb7 Mon Sep 17 00:00:00 2001 From: oriagmon Date: Tue, 16 Oct 2018 18:09:16 +0300 Subject: [PATCH 25/28] removed running main a few timees --- kube-hunter.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/kube-hunter.py b/kube-hunter.py index 77fa06c..44b8400 100755 --- a/kube-hunter.py +++ b/kube-hunter.py @@ -138,10 +138,5 @@ def main(): if __name__ == '__main__': - for i in range(6): - try: - main() - except: - import traceback - print ('\n\n\n\n\n\n\n\n\n') - traceback.print_exc() + main() + From b37ebf0fee540fec313fd0307e2483fcd070e381 Mon Sep 17 00:00:00 2001 From: oriagmon Date: Wed, 17 Oct 2018 10:44:34 +0300 Subject: [PATCH 26/28] Removed note & added parentheses to a return statement condition --- src/README.md | 2 -- src/modules/hunting/secrets.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/README.md b/src/README.md index 868eb65..e18c2fe 100644 --- a/src/README.md +++ b/src/README.md @@ -72,8 +72,6 @@ _The file's (module's) content is imported automatically"_ The second step is to determine what events your Hunter will subscribe to, and from where you can get them. `Convention:` Events should be declared in their corresponding module. for example, a KubeDashboardEvent event is declared in the dashboard discovery module. -`Note:` An hunter located under the `disovery` folder should not import any modules located under the `hunting` folder -in order to prevent circular dependency bugs Following the above example, let's figure out the imports: ```python diff --git a/src/modules/hunting/secrets.py b/src/modules/hunting/secrets.py index 9667a32..18f0587 100644 --- a/src/modules/hunting/secrets.py +++ b/src/modules/hunting/secrets.py @@ -33,7 +33,7 @@ class AccessSecrets(Hunter): logging.debug('Passive Hunter is attempting to access pod\'s secrets directory') # get all files and subdirectories files: self.secrets_evidence = [val for sublist in [[os.path.join(i[0], j) for j in i[2]] for i in os.walk('/var/run/secrets/')] for val in sublist] - return True if len(self.secrets_evidence) > 0 else False + return True if (len(self.secrets_evidence) > 0) else False def execute(self): if self.get_services(): From 54da07a73e1cb796ad7a39c36c04602562056cf9 Mon Sep 17 00:00:00 2001 From: oriagmon Date: Sun, 21 Oct 2018 11:23:30 +0300 Subject: [PATCH 27/28] Cleaned this branch to contain only updated secrets branch without locking --- kube-hunter.py | 9 --------- src/core/events/types/common.py | 6 ------ src/modules/report/collector.py | 3 --- src/modules/report/plain.py | 12 ------------ src/modules/report/yaml.py | 9 --------- 5 files changed, 39 deletions(-) diff --git a/kube-hunter.py b/kube-hunter.py index 44b8400..ee3bcd0 100755 --- a/kube-hunter.py +++ b/kube-hunter.py @@ -91,10 +91,7 @@ def list_hunters(): print("* {}\n {}\n".format( name, docs)) -tlock3 = threading.Lock() -tlock3.acquire() hunt_started = False -tlock3.release() def main(): @@ -112,10 +109,7 @@ def main(): if not any(scan_options): if not interactive_set_config(): return - tlock = threading.Lock() - tlock.acquire() hunt_started = True - tlock.release() handler.publish_event(HuntStarted()) handler.publish_event(HostScanEvent()) @@ -127,14 +121,11 @@ def main(): except EOFError: logging.error("\033[0;31mPlease run again with -it\033[0m") finally: - tlock2 = threading.Lock() - tlock2.acquire() if hunt_started: handler.publish_event(HuntFinished()) handler.join() handler.free() logging.debug("Cleaned Queue") - tlock2.release() if __name__ == '__main__': diff --git a/src/core/events/types/common.py b/src/core/events/types/common.py index d19dc2f..62590e9 100644 --- a/src/core/events/types/common.py +++ b/src/core/events/types/common.py @@ -65,10 +65,7 @@ class Vulnerability(object): def explain(self): return self.__doc__ -tlock1 = threading.Lock() -tlock1.acquire() event_id_count = 0 -tlock1.release() """ Discovery/Hunting Events """ @@ -78,11 +75,8 @@ class NewHostEvent(Event): global event_id_count self.host = host self.cloud = cloud - tlock = threading.Lock() - tlock.acquire() self.event_id = event_id_count event_id_count += 1 - tlock.release() def __str__(self): return str(self.host) diff --git a/src/modules/report/collector.py b/src/modules/report/collector.py index fa50cb0..db6b77f 100644 --- a/src/modules/report/collector.py +++ b/src/modules/report/collector.py @@ -38,8 +38,6 @@ class Collector(object): def execute(self): """function is called only when collecting data""" - tlock = threading.Lock() - tlock.acquire() global services, vulnerabilities bases = self.event.__class__.__mro__ if Service in bases: @@ -61,7 +59,6 @@ class Collector(object): port=self.event.port, desc=wrap_last_line(console_trim(self.event.explain(), '| ')) )) - tlock.release() class TablesPrinted(Event): diff --git a/src/modules/report/plain.py b/src/modules/report/plain.py index 64d2dda..37e1838 100644 --- a/src/modules/report/plain.py +++ b/src/modules/report/plain.py @@ -15,8 +15,6 @@ class PlainReporter(object): def get_report(self): """generates report tables""" output = "" - tlock = threading.Lock - tlock.acquire() if len(services): output += self.nodes_table() if not config.mapping: @@ -27,7 +25,6 @@ class PlainReporter(object): output += "\nNo vulnerabilities were found" else: print("\nKube Hunter couldn't find any clusters") - tlock.release() # print("\nKube Hunter couldn't find any clusters. {}".format("Maybe try with --active?" if not config.active else "")) return output @@ -41,13 +38,10 @@ class PlainReporter(object): nodes_table.header_style = "upper" # TODO: replace with sets id_memory = list() - tlock = threading.Lock - tlock.acquire() for service in services: if service.event_id not in id_memory: nodes_table.add_row(["Node/Master", service.host]) id_memory.append(service.event_id) - tlock.release() return "\nNodes\n{}\n".format(nodes_table) def services_table(self): @@ -58,11 +52,8 @@ class PlainReporter(object): services_table.sortby = "Service" services_table.reversesort = True services_table.header_style = "upper" - tlock = threading.Lock - tlock.acquire() for service in services: services_table.add_row([service.get_name(), "{}:{}{}".format(service.host, service.port, service.get_path()), service.explain()]) - tlock.release() return "\nDetected Services\n{}\n".format(services_table) def vulns_table(self): @@ -74,12 +65,9 @@ class PlainReporter(object): vuln_table.reversesort = True vuln_table.padding_width = 1 vuln_table.header_style = "upper" - tlock = threading.Lock - tlock.acquire() for vuln in vulnerabilities: row = ["{}:{}".format(vuln.host, vuln.port) if vuln.host else "", 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) - tlock.release() return "\nVulnerabilities\n{}\n".format(vuln_table) diff --git a/src/modules/report/yaml.py b/src/modules/report/yaml.py index 2a0b372..26a9e83 100644 --- a/src/modules/report/yaml.py +++ b/src/modules/report/yaml.py @@ -20,34 +20,25 @@ class YAMLReporter(object): def get_nodes(self): nodes = list() node_locations = set() - tlock = threading.Lock - tlock.acquire() for service in services: node_location = str(service.host) if node_location not in node_locations: nodes.append({"type": "Node/Master", "location": str(service.host)}) node_locations.add(node_location) - tlock.release() return nodes def get_services(self): - tlock = threading.Lock - tlock.acquire() services_data = [{"service": service.get_name(), "location": "{}:{}{}".format(service.host, service.port, service.get_path()), "description": service.explain()} for service in services] - tlock.release() return services_data def get_vulenrabilities(self): - tlock = threading.Lock - tlock.acquire() vulnerabilities_data = [{"location": "{}:{}".format(vuln.host, vuln.port) if vuln.host else "", "category": vuln.category.name, "vulnerability": vuln.get_name(), "description": vuln.explain(), "evidence": str(vuln.evidence)} for vuln in vulnerabilities] - tlock.release() return vulnerabilities_data From 590ba9d3f283647b65104153eabee1eb5df31946 Mon Sep 17 00:00:00 2001 From: oriagmon Date: Sun, 21 Oct 2018 11:57:58 +0300 Subject: [PATCH 28/28] Solved spacing conflict --- src/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/README.md b/src/README.md index e18c2fe..1b78d68 100644 --- a/src/README.md +++ b/src/README.md @@ -71,7 +71,9 @@ _The file's (module's) content is imported automatically"_ The second step is to determine what events your Hunter will subscribe to, and from where you can get them. `Convention:` Events should be declared in their corresponding module. for example, a KubeDashboardEvent event is declared in the dashboard discovery module. - + + `Note:` An hunter located under the `discovery` folder should not import any modules located under the `hunting` folder +in order to prevent circular dependency bug. Following the above example, let's figure out the imports: ```python