Skip to content

Commit

Permalink
Add spip_plugin_version function to retrieve plugin version from conf…
Browse files Browse the repository at this point in the history
…ig.txt or Composed-By header
  • Loading branch information
Chocapikk committed Sep 4, 2024
1 parent b8a1d40 commit 37042d8
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
66 changes: 66 additions & 0 deletions lib/msf/core/exploit/remote/http/spip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,71 @@ def spip_version

return version ? Rex::Version.new(version) : nil
end

# Determine Spip plugin version by name
#
# @param [String] plugin_name Name of the plugin to search for
# @return [Rex::Version, nil] Version of the plugin as Rex::Version, or nil if not found
def spip_plugin_version(plugin_name)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'spip.php')
)

return unless res

# Check the Composed-By header for plugin version or config.txt URL
composed_by = res.headers['Composed-By']
return unless composed_by

# Case 1: Look for config.txt URL in the header
if composed_by =~ %r{(https?://[^\s]+/local/config\.txt)}i
config_url = ::Regexp.last_match(1)
vprint_status("Found config.txt URL: #{config_url}")

# Fetch and parse the config.txt file directly
config_res = send_request_cgi(
'method' => 'GET',
'uri' => config_url
)

if config_res&.code == 200
return parse_plugin_version(config_res.body, plugin_name)
end
end

# Case 2: Check for plugin version directly in Composed-By
composed_by.split(',').each do |entry|
if entry =~ /#{plugin_name}\((\d+(\.\d+)+)\)/
return Rex::Version.new(::Regexp.last_match(1))
end
end

# Case 3: Fallback to fetching /local/config.txt directly
vprint_status('No version found in Composed-By header. Attempting to fetch /local/config.txt directly.')
config_url = normalize_uri(target_uri.path, 'local', 'config.txt')
config_res = send_request_cgi(
'method' => 'GET',
'uri' => config_url
)

return parse_plugin_version(config_res.body, plugin_name) if config_res&.code == 200

nil
end

# Parse the plugin version from config.txt or composed-by
#
# @param [String] body The body content to parse
# @param [String] plugin_name Name of the plugin to find the version for
# @return [Rex::Version, nil] Version of the plugin as Rex::Version, or nil if not found
def parse_plugin_version(body, plugin_name)
body.each_line do |line|
if line =~ /#{plugin_name}\((\d+(\.\d+)+)\)/
return Rex::Version.new(::Regexp.last_match(1))
end
end
nil
end
end
end
11 changes: 10 additions & 1 deletion modules/exploits/multi/http/spip_porte_plume_previsu_rce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,18 @@ def check
]

vulnerable_ranges.each do |range|
if rversion.between?(range[:start], range[:end])
next unless rversion.between?(range[:start], range[:end])

print_status('SPIP version is in the vulnerable range.')

plugin_version = spip_plugin_version('porte_plume')

unless plugin_version
print_warning('Could not determine the version of the porte_plume plugin.')
return Exploit::CheckCode::Appears("The detected SPIP version (#{rversion}) is vulnerable.")
end

return Exploit::CheckCode::Appears("The detected SPIP version (#{rversion}) and porte_plume version (#{plugin_version}) are vulnerable.") if plugin_version < Rex::Version.new('3.1.6')
end

return Exploit::CheckCode::Safe("The detected SPIP version (#{rversion}) is not vulnerable.")
Expand Down

0 comments on commit 37042d8

Please sign in to comment.