Why every scanner misses your worst access-control bugs

Broken access control has sat at the top of the OWASP Top 10 for years. It's the category behind a huge share of real-world breaches — the cross-tenant data leak, the "change the ID in the URL and read someone else's invoice," the low-privilege user who can call an admin function.

And it's the exact category that off-the-shelf DAST scanners are worst at. Not a little worse — structurally, fundamentally blind. Here's why, and what it takes to actually find these bugs.

The one-user problem

Most scanners crawl and test as a single authenticated session. They fire payloads at parameters, look for reflected input, error strings, timing differences — signature-based detection of injection-style flaws. That works reasonably well for XSS or SQLi, where the bug is visible in a single request/response pair.

Access control isn't like that. There's nothing wrong with the response to your request for your data. The vulnerability only appears when you compare what two different identities are allowed to do. A scanner that only holds one session has no second identity to compare against — so the bug is invisible to it by design.

Access control is a relational property. You can't detect it from a single vantage point, no matter how many payloads you throw.

The three shapes it takes

  • BOLA / IDOR (object level). Can user A read or modify user B's objects by referencing their identifier? GET /api/invoices/1043 when 1043 belongs to someone else.
  • BFLA (function level). Can a low-privilege role invoke a privileged function it should never reach? POST /api/users/role called by a normal user.
  • Tenant isolation. In a multi-tenant SaaS, does data or action leak across organizational boundaries — even when both sides are "legitimate" users?

All three share the same detection requirement: you need multiple authenticated identities, a map of what each can reach, and a systematic replay of every object and function across every other identity.

How to actually find them

Whether you do it by hand or with tooling, the method is the same:

  1. Authenticate as every role. Admin, manager, user, guest — and at least two separate tenants.
  2. Map each identity's surface independently. Walk the app as each role and record the endpoints, object IDs, and functions it legitimately touches.
  3. Cross-replay. Take every object ID and every function from one identity and replay it under every other identity's session — with that identity's own valid cookies, CSRF tokens, and headers, so a rejection means "access denied," not "malformed request."
  4. Diff the results. A 200 where you expected a 403 is the finding. So is a 403 that leaks whether the object exists.

That last point about request fidelity matters enormously. A hand-crafted request that's missing a cookie or a CSRF token will get rejected for the wrong reason — and you'll record a false negative, concluding the app is safe when it isn't. Every test request has to be a structurally valid request for that identity, with only the target object or function swapped.

A real one I found

This isn't hypothetical. In WireGuard Portal, the authenticated statistics WebSocket (/api/v0/ws) broadcast every peer's and interface's live traffic stats to any logged-in user — a textbook missing-per-user-authorization bug, and exactly the kind a single-session scanner can't see. I reported it, the maintainer fixed it in 2.3.0, and it was published as CVE-2026-54551. Same shape as the bugs above: nothing looks wrong in any single response — the flaw only appears when you compare what one identity can see against what it should.

Why I built this into hackz

Doing this by hand across a large app is slow and error-prone — hundreds of objects times several identities is a lot of careful replaying. So the multi-user access-control engine is the heart of hackz: it logs in as each role you define, auto-maps each one's surface, and cross-tests object- and function-level authorization systematically. It turns a tedious, mistake-prone manual pass into a repeatable scan — and surfaces exactly the BOLA, BFLA, and IDOR bugs generic scanners walk straight past.

The tooling handles the breadth. The manual work — the weird state machines, the chained primitives, the business-logic abuse — is where a human attacker still wins. That combination is what I bring to an engagement.

Worried about access control in your app?

It's the first thing I test — properly, across every role and tenant.

Book an assessment →

← Back to all posts