Ignore downstream version flag (#181)

* Ignore downstream version flag

This commit adds `--ignore-downstream` flag to kube-hunter.
Enabling the flag will make kube-hunter considering patched versions
as not vulnerable.
Resolves #179

* Add test cases and refine argument description
This commit is contained in:
Yehuda Chikvashvili
2019-09-19 21:57:39 +03:00
committed by danielsagi
parent c4e1e1e48c
commit a1feb06ec7
4 changed files with 53 additions and 5 deletions
+1
View File
@@ -9,6 +9,7 @@ parser.add_argument('--list', action="store_true", help="displays all tests in k
parser.add_argument('--interface', action="store_true", help="set hunting of all network interfaces")
parser.add_argument('--pod', action="store_true", help="set hunter as an insider pod")
parser.add_argument('--quick', action="store_true", help="Prefer quick scan (subnet 24)")
parser.add_argument('--ignore-downstream', action="store_true", help="Ignore patched kubernetes versions")
parser.add_argument('--cidr', type=str, help="set an ip range to scan, example: 192.168.0.0/16")
parser.add_argument('--mapping', action="store_true", help="outputs only a mapping of the cluster's nodes")
parser.add_argument('--remote', nargs='+', metavar="HOST", default=list(), help="one or more remote ip/dns to hunt")
+1
View File
@@ -6,6 +6,7 @@ parser.add_argument('--list', action="store_true", help="displays all tests in k
parser.add_argument('--interface', action="store_true", help="set hunting of all interface network interfaces")
parser.add_argument('--pod', action="store_true", help="set hunter as an insider pod")
parser.add_argument('--quick', action="store_true", help="Prefer quick scan (subnet 24)")
parser.add_argument('--ignore-downstream', action="store_true", help="Ignore patched kubernetes versions")
parser.add_argument('--cidr', type=str, help="set an ip range to scan, example: 192.168.0.0/16")
parser.add_argument('--mapping', action="store_true", help="outputs only a mapping of the cluster's nodes")
parser.add_argument('--remote', nargs='+', metavar="HOST", default=list(), help="one or more remote ip/dns to hunt")
+12 -3
View File
@@ -2,6 +2,8 @@ import logging
import json
import requests
from __main__ import config
from ...core.events import handler
from ...core.events.types import Vulnerability, Event, K8sVersionDisclosure
from ...core.types import Hunter, ActiveHunter, KubernetesCluster, RemoteCodeExec, AccessRisk, InformationDisclosure, \
@@ -95,8 +97,15 @@ class CveUtils:
return (v1>v2)-(v1<v2)
@staticmethod
def is_vulnerable(fix_versions, check_version):
def is_downstream_version(version):
return any(c in version for c in '+-~')
@staticmethod
def is_vulnerable(fix_versions, check_version, ignore_downstream=False):
"""Function determines if a version is vulnerable, by comparing to given fix versions by base release"""
if ignore_downstream and CveUtils.is_downstream_version(check_version):
return False
vulnerable = False
check_v = version.parse(check_version)
base_check_v = CveUtils.get_base_release(check_v)
@@ -146,7 +155,7 @@ class K8sClusterCveHunter(Hunter):
ServerApiClusterScopedResourcesAccess: ["1.13.9", "1.14.5", "1.15.2"]
}
for vulnerability, fix_versions in cve_mapping.items():
if CveUtils.is_vulnerable(fix_versions, self.event.version):
if CveUtils.is_vulnerable(fix_versions, self.event.version, config.ignore_downstream):
self.publish_event(vulnerability(self.event.version))
@@ -165,5 +174,5 @@ class KubectlCVEHunter(Hunter):
}
logging.debug('Kubectl Cve Hunter determining vulnerable version: {}'.format(self.event.version))
for vulnerability, fix_versions in cve_mapping.items():
if CveUtils.is_vulnerable(fix_versions, self.event.version):
if CveUtils.is_vulnerable(fix_versions, self.event.version, config.ignore_downstream):
self.publish_event(vulnerability(binary_version=self.event.version))
+39 -2
View File
@@ -3,7 +3,7 @@ import requests_mock
from src.core.events import handler
from src.core.events.types import K8sVersionDisclosure
from src.modules.hunting.cves import K8sClusterCveHunter, ServerApiVersionEndPointAccessPE, ServerApiVersionEndPointAccessDos
from src.modules.hunting.cves import K8sClusterCveHunter, ServerApiVersionEndPointAccessPE, ServerApiVersionEndPointAccessDos, CveUtils
cve_counter = 0
@@ -49,4 +49,41 @@ class test_CVE_2018_1002105(object):
class test_CVE_2019_1002100(object):
def __init__(self, event):
global cve_counter
cve_counter += 1
cve_counter += 1
class test_CveUtils(object):
def test_is_downstream():
test_cases = (
('1', False),
('1.2', False),
('1.2-3', True),
('1.2-r3', True),
('1.2+3', True),
('1.2~3', True),
('1.2+a3f5cb2', True),
('1.2-9287543', True),
('v1', False),
('v1.2', False),
('v1.2-3', True),
('v1.2-r3', True),
('v1.2+3', True),
('v1.2~3', True),
('v1.2+a3f5cb2', True),
('v1.2-9287543', True),
('v1.13.9-gke.3', True)
)
for version, expected in test_cases:
actual = CveUtils.is_downstream_version(version)
assert actual == expected
def test_ignore_downstream():
test_cases = (
('v2.2-abcd', ['v1.1', 'v2.3'], False),
('v2.2-abcd', ['v1.1', 'v2.2'], False),
('v1.13.9-gke.3', ['v1.14.8'], False)
)
for check_version, fix_versions, expected in test_cases:
actual = CveUtils.is_vulnerable(check_version, fix_versions, True)
assert actual == expected