CVE-2024-23334: Verified Repro With Script Download
CVE-2024-23334: aiohttp static file directory traversal via follow symlinks
CVE-2024-23334 is verified against aiohttp · pip. Affected versions: >= 1.0.5, < 3.9.2. Fixed in 3.9.2. Vulnerability class: Path Traversal. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00199.
What Is CVE-2024-23334?
CVE-2024-23334 is a high-severity path traversal vulnerability (CWE-22, CVSS 8.2) in aiohttp's static file handler that allows attackers to read arbitrary files outside the configured static root. Pruva reproduced it (reproduction REPRO-2026-00199).
CVE-2024-23334 Severity & CVSS Score
CVE-2024-23334 is rated high severity, with a CVSS base score of 8.2 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected aiohttp Versions
aiohttp · pip versions >= 1.0.5, < 3.9.2 are affected.
How to Reproduce CVE-2024-23334
pruva-verify REPRO-2026-00199 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00199/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2024-23334
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
URL path with ../ traversal sequences sent via HTTP GET
- HTTP GET /static/../poc-aiohttp-test.txt to aiohttp web.static() route with follow_symlinks=True
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for CVE-2024-23334
Vulnerability Summary
CVE: CVE-2024-23334 Package: aiohttp (pip) Vulnerable versions: >= 1.0.5, < 3.9.2 Patched version: 3.9.2 CWE: CWE-22 (Path Traversal) Severity: High
Description
aiohttp's web.static() handler allows directory traversal when
follow_symlinks=True is configured. Path traversal sequences (../)
in the URL can escape the configured static root directory and read
arbitrary files on the server filesystem, even when no symlinks are
present.
Root Cause
The vulnerability is in StaticResource._handle() in
aiohttp/web_urldispatcher.py.
Vulnerable code (aiohttp 3.9.1)
async def _handle(self, request: Request) -> StreamResponse:
rel_url = request.match_info["filename"]
try:
filename = Path(rel_url)
if filename.anchor:
raise HTTPForbidden()
filepath = self._directory.joinpath(filename).resolve()
if not self._follow_symlinks:
filepath.relative_to(self._directory)
except (ValueError, FileNotFoundError) as error:
raise HTTPNotFound() from error
When follow_symlinks=True:
filename = Path(rel_url)creates a Path from the URL filename (e.g.,../poc-aiohttp-test.txt)filename.anchoronly checks for absolute paths;../...is relative, so it passesfilepath = self._directory.joinpath(filename).resolve()joins the directory with the traversal path and resolves..sequences to the real filesystem path outside the static rootif not self._follow_symlinks:is False whenfollow_symlinks=True, so therelative_to(self._directory)validation is skipped entirely- The code proceeds to serve the file at the escaped path
Fixed code (aiohttp 3.9.2)
async def _handle(self, request: Request) -> StreamResponse:
rel_url = request.match_info["filename"]
try:
filename = Path(rel_url)
if filename.anchor:
raise HTTPForbidden()
unresolved_path = self._directory.joinpath(filename)
if self._follow_symlinks:
normalized_path = Path(os.path.normpath(unresolved_path))
normalized_path.relative_to(self._directory)
filepath = normalized_path.resolve()
else:
filepath = unresolved_path.resolve()
filepath.relative_to(self._directory)
except (ValueError, FileNotFoundError) as error:
raise HTTPNotFound() from error
The fix adds a relative_to(self._directory) check even when
follow_symlinks=True. It normalizes the path with
os.path.normpath() first (resolving .. sequences without
following symlinks), then validates it stays within the directory
before resolving symlinks. If the path escapes the directory,
relative_to() raises ValueError which is caught and results in
HTTPNotFound (404).
Reproduction Evidence
Environment
- Python 3.11 (via uv standalone build)
- aiohttp 3.9.1 (vulnerable) and 3.9.2 (fixed)
- Server:
web.static("/static", "static/", follow_symlinks=True) - Probe file:
poc-aiohttp-test.txtplaced as a sibling of the static root
Results
Vulnerable aiohttp 3.9.1:
- HTTP GET
/static/../poc-aiohttp-test.txtwithcurl --path-as-isor raw-socket HTTP -> HTTP 200, body contains probe content - Traversal variants include raw
../,%2f,%2e,%2F, and a raw HTTP socket request - Leak method: raw_path_as_is
- Both vulnerable attempts leaked
Fixed aiohttp 3.9.2:
- Same requests -> HTTP 404 Not Found for all variants
- Both fixed attempts blocked
Arbitrary file read via HTTP path traversal. An attacker can read
any file accessible to the aiohttp server process by sending crafted
HTTP requests with ../ sequences to a static route configured with
follow_symlinks=True.
Surface and Impact Classification
The ticket metadata claims claimed_surface=converter_document and
expected_impact=code_execution. However:
- Actual surface:
api_remote-- the vulnerability is exploited via HTTP requests to a running aiohttp web server, not a document converter. - Actual impact:
info_leak-- the vulnerability allows arbitrary file read (path traversal), not code execution.
The vulnerability is real and confirmed at the api_remote surface,
but the claim metadata misclassifies both the surface and the impact.
References
CVE-2024-23334 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
python3 --version && pip3 --version && which curl && which jqPython 3.14.4 pip 25.1.1 from /usr/lib/python3/dist-packages/pip (python 3.14) /usr/bin/curl /usr/bin/jq
ls /usr/bin/python* 2>/dev/null; ls /usr/local/bin/python* 2>/dev/null; which python3.11 python3.10 python3.12 python3.13 2>/dev/null; echo "---pyenv---"; which pyenv 2>/dev/null; echo "---deadsnakes---"; ls /usr/bin/python3.1* 2>/dev/null/usr/bin/python /usr/bin/python3 /usr/bin/python3-config /usr/bin/python3.14 /usr/bin/python3.14-config ---pyenv--- ---deadsnakes--- /usr/bin/python3.14
cd /tmp && python3 -m venv testvenv 2>&1 | head -5 && /tmp/testvenv/bin/pip install --dry-run aiohttp==3.9.1 2>&1 | tail -20{"cwd":"/data/pruva/runs/2fe67f5c-a344-41e3-887b-3d210ff0947f","duration_ms":14364,"exit_code":0,"stderr":"","stdout":"Collecting multidict<7.0,>=4.5 (from aiohttp==3.9.1)\n Downloading multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (5.3 kB)\nCollecting yarl<2.0,>=1.0 (from aiohttp==3.9.1)\n Downloading yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (94 kB)\nCollecting frozenlist>=1.1.1 (from aiohttp==3.9.1)\n Downloading frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl.metadata (20 kB)\nCollecting aiosignal>=1.1.2 (from aiohttp==3.9.1)\n Downloading aiosignal-1.4.0-py3-none-any.whl.metadata (3.7 kB)\nCollecting idna>=2.0 (from yarl<2.0,>=1.0->aiohttp==3.9.1)\n Downloading idna-3.18-py3-none-any.whl.metadata (6.1 kB)\nCollecting propcache>=0.2.1 (from yarl<2.0,>=1.0->aiohttp==3.9.1)\n Downloading propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (16 kB)\nDownloading multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (24… [truncated]#!/bin/bash
set -euo pipefail
cd /tmp
rm -rf testvenv
python3 -m venv testvenv
/tmp/testvenv/bin/pip install aiohttp==3.9.1 2>&1 | tail -30
echo "EXIT: $?"
/tmp/testvenv/bin/python -c "import aiohttp; print('aiohttp', aiohttp.__version__)"
Unknown error
Artifacts and Evidence for CVE-2024-23334
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2024-23334
Upgrade aiohttp · pip to 3.9.2 or later.
FAQ: CVE-2024-23334
How does the CVE-2024-23334 path traversal work?
web.static(..., follow_symlinks=True), the handler's path validation can be bypassed with traversal sequences in the requested URL, letting an attacker read arbitrary files readable by the aiohttp process instead of only files under the intended static directory.Which aiohttp versions are affected by CVE-2024-23334, and where is it fixed?
How severe is CVE-2024-23334?
How can I reproduce CVE-2024-23334?
web.static() route configured using follow_symlinks=True. It sends a crafted request with directory-traversal sequences and shows the server returning contents of a file outside the static root.References for CVE-2024-23334
Authoritative sources for CVE-2024-23334 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.