Repository navigation
Authz Diffing Playbook
claviger replay sends the same request (or a whole set of requests) under several
identities at once and prints a status matrix. It reports facts: the status, the
size, and where identities disagree. It does not decide whether a difference is a
vulnerability; you do that by eye or by piping the output into your own diff.
claviger replay --as admin --as user-a --as anon --path /api/records/42
IDENTITY STATUS SIZE TIME
admin 200 412 1.2ms
user-a 403 21 0.9ms
anon 401 27 0.7ms
Read it top to bottom. The expected shape for an owner-only record is
owner 200 / other 403 / anon 401. Anything else is worth a look:
-
user-areturns200on a record it does not own: horizontal privilege escalation (IDOR). -
anonreturns200: a missing authentication check. -
user-aandadminreturn the same size on a200: the low-privilege user may be seeing admin-only data.
anon is a reserved identity name meaning "send no session at all"; you do not
configure it.
Sweep many endpoints in one run with --corpus. Three formats are accepted, auto
detected by extension or forced with --format:
claviger replay --corpus endpoints.txt --as admin --as user-a --as anon
claviger replay --corpus session.har --as admin --as user-a --as anon
claviger replay --corpus openapi.yaml --include-unsafe --as admin --as user-a
-
requests file: one
METHOD /pathper line;#comments allowed. -
HAR export: captured from a browser or proxy. Claviger strips the captured
identity headers (
Cookie,Authorization,Proxy-Authorization,X-CSRF-Token) at load, so each identity is tested with its own session and the capturing user's credentials do not leak into the comparison. -
OpenAPI spec: one request per operation. Only safe methods (GET/HEAD/OPTIONS)
are included by default;
--include-unsafeadds POST/PUT/PATCH/DELETE.
The matrix marks a row DIFFERS when the non-error statuses across identities are not
all equal:
GET /api/records/42 DIFFERS
admin 200 412
user-a 403 21
anon 401 27
GET /api/records/42/history DIFFERS
admin 200 980
user-a 200 980
anon 401 27
The second row is the finding: user-a gets the same 200 and size as admin on a
history endpoint, so the object-level check is missing there.
A fast workflow: browse the app as an admin through Burp, export the proxy history
(or a specific set of requests) as HAR, then replay that HAR under a low-privilege
identity and anon. Because Claviger strips the captured credentials, the admin's
own cookies do not carry over; every row is a clean test of whether the lower
identity should have reached that endpoint.
claviger replay --corpus admin-session.har --as admin --as user-a --as anon
Scan the output for any user-a or anon row that is not blocked where it should
be.
replay exits non-zero only when every request failed for every identity (for
example the target is down). A row that returns a 200, 403, or 401 is a
successful measurement, not an error, so the exit code stays 0; the findings are in
the table, which is the deliverable.