Merge branch 'master' into access-secrets-hunter

This commit is contained in:
Ori Agmon
2018-10-21 12:04:59 +03:00
committed by GitHub
6 changed files with 156 additions and 7 deletions

View File

@@ -41,7 +41,7 @@ class EventQueue(Queue, object):
self.subscribe_event(event, hook=hook, predicate=predicate)
return hook
return wrapper
# getting uninstantiated event object
def subscribe_event(self, event, hook=None, predicate=None):
if ActiveHunter in hook.__mro__:
@@ -75,13 +75,13 @@ class EventQueue(Queue, object):
hook = self.get()
try:
hook.execute()
except Exception as ex:
except Exception as ex:
logging.debug(ex.message)
self.task_done()
logging.debug("closing thread...")
def notifier(self):
time.sleep(2)
time.sleep(2)
while self.unfinished_tasks > 0:
logging.debug("{} tasks left".format(self.unfinished_tasks))
time.sleep(3)
@@ -91,5 +91,5 @@ class EventQueue(Queue, object):
self.running = False
with self.mutex:
self.queue.clear()
handler = EventQueue(800)

View File

@@ -0,0 +1,28 @@
import json
import logging
import requests
from ...core.events import handler
from ...core.events.types import Event, OpenPortEvent, Service
from ...core.types import Hunter
# Service:
class EtcdAccessEvent(Service, Event):
"""Etcd is a DB that stores cluster's data, it contains configuration and current state information, and might contain secrets"""
def __init__(self):
Service.__init__(self, name="Etcd")
@handler.subscribe(OpenPortEvent, predicate= lambda p: p.port == 2379)
class EtcdRemoteAccess(Hunter):
"""Etcd Remote Access
Checks for remote availability of etcd, version, read access, write access
"""
def __init__(self, event):
self.event = event
def execute(self):
self.publish_event(EtcdAccessEvent())

View File

@@ -15,7 +15,6 @@ from ...core.events import handler
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'

View File

@@ -7,7 +7,7 @@ from ...core.events import handler
from ...core.events.types import NewHostEvent, OpenPortEvent
default_ports = [8001, 10250, 10255, 30000, 443, 6443]
default_ports = [8001, 10250, 10255, 30000, 443, 6443, 2379]
@handler.subscribe(NewHostEvent)
class PortDiscovery(Hunter):

View File

@@ -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, IdentityTheft, Azure
class AzureSpnExposure(Vulnerability, Event):

122
src/modules/hunting/etcd.py Normal file
View File

@@ -0,0 +1,122 @@
import logging
import requests
from ...core.events import handler
from ...core.events.types import Vulnerability, Event, OpenPortEvent
from ...core.types import ActiveHunter, Hunter, KubernetesCluster, InformationDisclosure, RemoteCodeExec, \
UnauthenticatedAccess, AccessRisk
""" Vulnerabilities """
class EtcdRemoteWriteAccessEvent(Vulnerability, Event):
"""Remote write access might grant an attacker full control over the kubernetes cluster"""
def __init__(self, write_res):
Vulnerability.__init__(self, KubernetesCluster, name="Etcd Remote Write Access Event", category=RemoteCodeExec)
self.evidence = write_res
class EtcdRemoteReadAccessEvent(Vulnerability, Event):
"""Remote read access might expose to an attacker cluster's possible exploits, secrets and more."""
def __init__(self, keys):
Vulnerability.__init__(self, KubernetesCluster, name="Etcd Remote Read Access Event", category=AccessRisk)
self.evidence = keys
class EtcdRemoteVersionDisclosureEvent(Vulnerability, Event):
"""Remote version disclosure might give an attacker a valuable data to attack a cluster"""
def __init__(self, version):
Vulnerability.__init__(self, KubernetesCluster, name="Etcd Remote version disclosure",
category=InformationDisclosure)
self.evidence = version
class EtcdAccessEnabledWithoutAuthEvent(Vulnerability, Event):
"""Etcd is accessible using HTTP (without authorization and authentication), it would allow a potential attacker to
gain access to the etcd"""
def __init__(self, version):
Vulnerability.__init__(self, KubernetesCluster, name="Etcd is accessible using insecure connection (HTTP)",
category=UnauthenticatedAccess)
self.evidence = version
# Active Hunter
@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 2379)
class EtcdRemoteAccessActive(ActiveHunter):
"""Etcd Remote Access
Checks for remote write access to etcd"""
def __init__(self, event):
self.event = event
self.write_evidence = ''
def db_keys_write_access(self):
logging.debug("Active hunter is attempting to write keys remotely on host " + self.event.host)
data = {
'value': 'remotely written data'
}
try:
r = requests.post("{protocol}://{host}:{port}/v2/keys/message".format(host=self.event.host, port=2379,
protocol=self.protocol), data=data)
self.write_evidence = r.content if r.status_code == 200 and r.content != '' else False
return self.write_evidence
except requests.exceptions.ConnectionError:
return False
def execute(self):
if self.db_keys_write_access():
self.publish_event(EtcdRemoteWriteAccessEvent(self.write_evidence))
# Passive Hunter
@handler.subscribe(OpenPortEvent, predicate=lambda p: p.port == 2379)
class EtcdRemoteAccess(Hunter):
"""Etcd Remote Access
Checks for remote availability of etcd, version, read access, write access
"""
def __init__(self, event):
self.event = event
self.version_evidence = ''
self.keys_evidence = ''
self.protocol = 'https'
def db_keys_disclosure(self):
logging.debug(self.event.host + " Passive hunter is attempting to read etcd keys remotely")
try:
r = requests.get(
"{protocol}://{host}:{port}/v2/keys".format(protocol=self.protocol, host=self.event.host, port=2379),
verify=False)
self.keys_evidence = r.content if r.status_code == 200 and r.content != '' else False
return self.keys_evidence
except requests.exceptions.ConnectionError:
return False
def version_disclosure(self):
logging.debug(self.event.host + " Passive hunter is attempting to check etcd version remotely")
try:
r = requests.get(
"{protocol}://{host}:{port}/version".format(protocol=self.protocol, host=self.event.host, port=2379),
verify=False)
self.version_evidence = r.content if r.status_code == 200 and r.content != '' else False
return self.version_evidence
except requests.exceptions.ConnectionError:
return False
def insecure_access(self):
logging.debug(self.event.host + " Passive hunter is attempting to access etcd insecurely")
try:
r = requests.get("http://{host}:{port}/version".format(host=self.event.host, port=2379), verify=False)
return r.content if r.status_code == 200 and r.content != '' else False
except requests.exceptions.ConnectionError:
return False
def execute(self):
if self.insecure_access(): # make a decision between http and https protocol
self.protocol = 'http'
if self.version_disclosure():
self.publish_event(EtcdRemoteVersionDisclosureEvent(self.version_evidence))
if self.protocol == 'http':
self.publish_event(EtcdAccessEnabledWithoutAuthEvent(self.version_evidence))
if self.db_keys_disclosure():
self.publish_event(EtcdRemoteReadAccessEvent(self.keys_evidence))