Skip to content

feat(scan): --method-override mutator for HTTP verb-tampering 403/401 bypass#21

Merged
bugsyhewitt merged 1 commit into
mainfrom
worker-r24-possession
May 29, 2026
Merged

feat(scan): --method-override mutator for HTTP verb-tampering 403/401 bypass#21
bugsyhewitt merged 1 commit into
mainfrom
worker-r24-possession

Conversation

@bugsyhewitt

Copy link
Copy Markdown
Owner

Summary

Adds the HTTP verb / method-override access-control bypass mutator (--method-override), the canonical "method bypass" technique that completes possession's access-control bypass family alongside --forbidden-bypass (path matching) and --csrf-header (anti-CSRF token). It attacks which HTTP verb the access-control layer evaluates.

This was chosen over the alternative POST_V01 option (HTTP/3 QUIC probing): QUIC would require a transport-layer rewrite of the replay engine, violating the "don't change the defining architecture" constraint, whereas method-override is a pure mutator that drops cleanly into the existing pure-mutator + comparative-ladder design. POST_V01.md does not list either option (it is stale relative to recent rotations), so this fills the assessed gap directly. Verified absent in the codebase before implementing.

Three technique families (every variant keeps the caller's own credentials — no identity swap)

  • override-header — inject X-HTTP-Method, X-HTTP-Method-Override, or X-Method-Override naming a verb that crosses the safe/unsafe boundary (safe GET/HEAD/OPTIONS → POST; any write → GET), while leaving the request-line verb unchanged. Frameworks that honour the override header dispatch the overridden verb to the handler the gateway gated by request-line method.
  • verb-swap — change the actual request-line method to a sibling verb the gateway may not gate while the handler still serves it (GET ↔ HEAD/OPTIONS/POST, writes ↔ GET/PUT/PATCH). The original verb is never re-emitted (no no-op swap).
  • case-toggle — flip the verb case (GETget) to defeat a case-sensitive gateway matcher fronting a case-insensitive router.

Properties

  • Findings are class authz-bypass (ASVS V8.3.x, severity high), riding the existing comparative ladder unchanged.
  • Pure and deterministic (verbs are constants, techniques sorted) — --dry-run and the offline corpus cover it for free.
  • Off by default (verb-swaps re-issue under state-changing methods), registered inert so the canonical DefaultRegistry order — and its order test — is unchanged.
  • The replay engine already issues requests via base.Method, so verb swaps work end-to-end with zero engine changes.

Tests

  • 14 new test functions (12 in internal/mutate/method_override_test.go + 2 buildRegistry gating tests in internal/cli): off-by-default, nil-safe, credential-preservation, header/verb-swap/case-toggle technique coverage, empty + unknown-verb handling, determinism, sorted-order, and toggleMethodCase unit coverage.
  • All existing buildRegistry call sites updated for the new trailing argument.
  • go test ./... -race green; go vet and gofmt clean.

Test plan

  • go build ./...
  • go test ./... -race (all packages pass)
  • go vet ./... clean
  • gofmt -l clean on all touched files
  • --method-override --dry-run emits header + verb-swap + case-toggle variants on the example corpus
  • without the flag, zero method-override variants are produced (off-by-default corpus guarantee)

🤖 Generated with Claude Code

…/401 bypass

Adds the HTTP verb / method-override access-control bypass mutator, the
canonical "method bypass" technique that completes the access-control bypass
family alongside --forbidden-bypass (path matching) and --csrf-header (CSRF
token). It attacks which HTTP verb the access-control layer evaluates.

Three technique families, every variant keeping the caller's own credentials
(no identity swap):
- override-header: inject X-HTTP-Method / X-HTTP-Method-Override /
  X-Method-Override naming a verb that crosses the safe/unsafe boundary while
  leaving the request-line verb unchanged
- verb-swap: change the request-line method to a sibling verb the gateway may
  not gate (GET <-> HEAD/OPTIONS/POST, writes <-> GET/PUT/PATCH); never
  re-emits the original verb
- case-toggle: flip the verb case (GET -> get) to defeat case-sensitive matchers

Findings are class authz-bypass (ASVS V8.3.x, severity high), riding the
existing comparative ladder unchanged. Pure/deterministic so --dry-run and the
offline corpus cover it for free. Off by default (verb-swaps re-issue under
state-changing methods), registered inert so DefaultRegistry order is unchanged.

Tests: 14 new test functions (12 mutator + 2 buildRegistry gating). All
existing buildRegistry call sites updated for the new signature argument.
README and CHANGELOG updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bugsyhewitt bugsyhewitt merged commit 86db319 into main May 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant