diff --git a/src/modules/discovery/apiserver.py b/src/modules/discovery/apiserver.py index 02035c5..86c5a8e 100644 --- a/src/modules/discovery/apiserver.py +++ b/src/modules/discovery/apiserver.py @@ -12,17 +12,19 @@ class ApiServer(Service, Event): Service.__init__(self, name="API Server") +# Other devices could have this port open, but we can check to see if it looks like a Kubernetes node +# A Kubernetes API server will respond with a JSON message that includes a "code" field for the HTTP status code @handler.subscribe(OpenPortEvent, predicate=lambda x: x.port==443 or x.port==6443) class ApiServerDiscovery(Hunter): """Api Server Discovery - Checks for the existence of a an Api Server + Checks for the existence of a an API Server """ def __init__(self, event): self.event = event def execute(self): - logging.debug("Attempting to discover an Api server") + logging.debug("Attempting to discover an API server") main_request = requests.get("https://{}:{}".format(self.event.host, self.event.port), verify=False).text - if "code" in main_request: + if '"code"' in main_request: self.event.role = "Master" - self.publish_event(ApiServer()) + self.publish_event(ApiServer()) diff --git a/src/modules/discovery/hosts.py b/src/modules/discovery/hosts.py index 3912ba1..b942ce3 100644 --- a/src/modules/discovery/hosts.py +++ b/src/modules/discovery/hosts.py @@ -24,12 +24,12 @@ class RunningAsPodEvent(Event): def get_auth_token(self): try: - with open("/run/secrets/kubernetes.io/serviceaccount/token") as token_file: + with open("/var/run/secrets/kubernetes.io/serviceaccount/token") as token_file: return token_file.read() except IOError: pass def get_client_cert(self): - return "/run/secrets/kubernetes.io/serviceaccount/ca.crt" + return "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" class AzureMetadataApi(Vulnerability, Event): """Access to the Azure Metadata API exposes information about the machines associated with the cluster""" diff --git a/src/modules/discovery/test_apiserver.py b/src/modules/discovery/test_apiserver.py new file mode 100644 index 0000000..006b48b --- /dev/null +++ b/src/modules/discovery/test_apiserver.py @@ -0,0 +1,27 @@ +import requests_mock + +from apiserver import ApiServer, ApiServerDiscovery +from ...core.events.types import Event +from ...core.events import handler + +def test_ApiServer(): + + with requests_mock.Mocker() as m: + m.get('https://mockOther:443', text='elephant') + m.get('https://mockKubernetes:443', text='{"code":403}') + + e = Event() + e.port = 443 + e.host = 'mockOther' + + a = ApiServerDiscovery(e) + a.execute() + + e.host = 'mockKubernetes' + a.execute() + +# We should only generate an ApiServer event for a response that looks like it came from a Kubernetes node +@handler.subscribe(ApiServer) +class testApiServer(object): + def __init__(self, event): + assert event.host == 'mockKubernetes' diff --git a/src/modules/hunting/CVE_2018_1002105.py b/src/modules/hunting/CVE_2018_1002105.py index c45468f..ae2b4b1 100644 --- a/src/modules/hunting/CVE_2018_1002105.py +++ b/src/modules/hunting/CVE_2018_1002105.py @@ -5,7 +5,8 @@ import uuid import ast from ...core.events import handler -from ...core.events.types import Vulnerability, Event, OpenPortEvent +from ...core.events.types import Vulnerability, Event +from ..discovery.apiserver import ApiServer from ...core.types import Hunter, ActiveHunter, KubernetesCluster, RemoteCodeExec, AccessRisk, InformationDisclosure, PrivilegeEscalation """ Vulnerabilities """ @@ -17,7 +18,7 @@ class ServerApiVersionEndPointAccess(Vulnerability, Event): self.evidence = evidence # Passive Hunter -@handler.subscribe(OpenPortEvent, predicate=lambda x: x.port == 443 or x.port == 6443) +@handler.subscribe(ApiServer) class IsVulnerableToCVEAttack(Hunter): """ Node is running a Kubernetes version vulnerable to critical CVE-2018-1002105 """ diff --git a/src/modules/hunting/apiserver.py b/src/modules/hunting/apiserver.py index b8de74b..8975d21 100644 --- a/src/modules/hunting/apiserver.py +++ b/src/modules/hunting/apiserver.py @@ -4,7 +4,8 @@ import requests import uuid from ...core.events import handler -from ...core.events.types import Vulnerability, Event, OpenPortEvent +from ...core.events.types import Vulnerability, Event +from ..discovery.apiserver import ApiServer from ...core.types import Hunter, ActiveHunter, KubernetesCluster, RemoteCodeExec, AccessRisk, InformationDisclosure @@ -203,7 +204,7 @@ class ApiServerPassiveHunterFinished(Event): # Passive Hunter -@handler.subscribe(OpenPortEvent, predicate=lambda x: x.port == 443 or x.port == 6443) +@handler.subscribe(ApiServer) class AccessApiServerViaServiceAccountToken(Hunter): """ API Server Hunter Accessing the API server within a compromised pod might grant an attacker full control over the cluster