mirror of
https://github.com/aquasecurity/kube-hunter.git
synced 2026-04-18 08:26:57 +00:00
Compare commits
5 Commits
fix_lint_c
...
change_lin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d20e5c891e | ||
|
|
72360c6043 | ||
|
|
b0e88d9e49 | ||
|
|
432a602530 | ||
|
|
6116c83ec7 |
@@ -1,23 +0,0 @@
|
||||
---
|
||||
vid: KHV052
|
||||
title: Exposed Pods
|
||||
categories: [Information Disclosure]
|
||||
---
|
||||
|
||||
# {{ page.vid }} - {{ page.title }}
|
||||
|
||||
## Issue description
|
||||
|
||||
An attacker could view sensitive information about pods that are bound to a Node using the exposed /pods endpoint
|
||||
This can be done either by accessing the readonly port (default 10255), or from the secure kubelet port (10250)
|
||||
|
||||
## Remediation
|
||||
|
||||
Ensure kubelet is protected using `--anonymous-auth=false` kubelet flag. Allow only legitimate users using `--client-ca-file` or `--authentication-token-webhook` kubelet flags. This is usually done by the installer or cloud provider.
|
||||
|
||||
Disable the readonly port by using `--read-only-port=0` kubelet flag.
|
||||
|
||||
## References
|
||||
|
||||
- [Kubelet configuration](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)
|
||||
- [Kubelet authentication/authorization](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 111 KiB |
@@ -35,7 +35,10 @@ class ExposedPodsHandler(Vulnerability, Event):
|
||||
|
||||
def __init__(self, pods):
|
||||
Vulnerability.__init__(
|
||||
self, component=Kubelet, name="Exposed Pods", category=InformationDisclosure, vid="KHV052"
|
||||
self,
|
||||
component=Kubelet,
|
||||
name="Exposed Pods",
|
||||
category=InformationDisclosure,
|
||||
)
|
||||
self.pods = pods
|
||||
self.evidence = f"count: {len(self.pods)}"
|
||||
@@ -344,23 +347,27 @@ class SecureKubeletPortHunter(Hunter):
|
||||
|
||||
# need further investigation on websockets protocol for further implementation
|
||||
def test_port_forward(self):
|
||||
pass
|
||||
config = get_config()
|
||||
headers = {
|
||||
"Upgrade": "websocket",
|
||||
"Connection": "Upgrade",
|
||||
"Sec-Websocket-Key": "s",
|
||||
"Sec-Websocket-Version": "13",
|
||||
"Sec-Websocket-Protocol": "SPDY",
|
||||
}
|
||||
pf_url = self.path + KubeletHandlers.PORTFORWARD.value.format(
|
||||
pod_namespace=self.pod["namespace"],
|
||||
pod_id=self.pod["name"],
|
||||
port=80,
|
||||
)
|
||||
self.session.get(
|
||||
pf_url,
|
||||
headers=headers,
|
||||
verify=False,
|
||||
stream=True,
|
||||
timeout=config.network_timeout,
|
||||
).status_code == 200
|
||||
# TODO: what to return?
|
||||
# Example starting code:
|
||||
#
|
||||
# config = get_config()
|
||||
# headers = {
|
||||
# "Upgrade": "websocket",
|
||||
# "Connection": "Upgrade",
|
||||
# "Sec-Websocket-Key": "s",
|
||||
# "Sec-Websocket-Version": "13",
|
||||
# "Sec-Websocket-Protocol": "SPDY",
|
||||
# }
|
||||
# pf_url = self.path + KubeletHandlers.PORTFORWARD.value.format(
|
||||
# pod_namespace=self.pod["namespace"],
|
||||
# pod_id=self.pod["name"],
|
||||
# port=80,
|
||||
# )
|
||||
|
||||
# executes one command and returns output
|
||||
def test_run_container(self):
|
||||
@@ -371,9 +378,8 @@ class SecureKubeletPortHunter(Hunter):
|
||||
container_name="test",
|
||||
cmd="",
|
||||
)
|
||||
# if we get this message, we know we passed Authentication and Authorization, and that the endpoint is enabled.
|
||||
status_code = self.session.post(run_url, verify=False, timeout=config.network_timeout).status_code
|
||||
return status_code == requests.codes.NOT_FOUND
|
||||
# if we get a Method Not Allowed, we know we passed Authentication and Authorization.
|
||||
return self.session.get(run_url, verify=False, timeout=config.network_timeout).status_code == 405
|
||||
|
||||
# returns list of currently running pods
|
||||
def test_running_pods(self):
|
||||
@@ -1133,16 +1139,11 @@ class ProveSystemLogs(ActiveHunter):
|
||||
f"{self.base_url}/" + KubeletHandlers.LOGS.value.format(path="audit/audit.log"),
|
||||
verify=False,
|
||||
timeout=config.network_timeout,
|
||||
)
|
||||
|
||||
# TODO: add more methods for proving system logs
|
||||
if audit_logs.status_code == requests.status_codes.codes.OK:
|
||||
logger.debug(f"Audit log of host {self.event.host}: {audit_logs.text[:10]}")
|
||||
# iterating over proctitles and converting them into readable strings
|
||||
proctitles = []
|
||||
for proctitle in re.findall(r"proctitle=(\w+)", audit_logs.text):
|
||||
proctitles.append(bytes.fromhex(proctitle).decode("utf-8").replace("\x00", " "))
|
||||
self.event.proctitles = proctitles
|
||||
self.event.evidence = f"audit log: {proctitles}"
|
||||
else:
|
||||
self.event.evidence = "Could not parse system logs"
|
||||
).text
|
||||
logger.debug(f"Audit log of host {self.event.host}: {audit_logs[:10]}")
|
||||
# iterating over proctitles and converting them into readable strings
|
||||
proctitles = []
|
||||
for proctitle in re.findall(r"proctitle=(\w+)", audit_logs):
|
||||
proctitles.append(bytes.fromhex(proctitle).decode("utf-8").replace("\x00", " "))
|
||||
self.event.proctitles = proctitles
|
||||
self.event.evidence = f"audit log: {proctitles}"
|
||||
|
||||
Reference in New Issue
Block a user