CVE-2026-28414
Gradio path traversal on Win/Py3.13+ allows unauthenticated file read.
- CVSS 7.5
- CWE-22
- Input Validation and Sanitization
- Remote
Gradio is an open-source Python package designed for quick prototyping. Prior to version 6.7, Gradio apps running on Window with Python 3.13+ are vulnerable to an absolute path traversal issue that enables unauthenticated attackers to read arbitrary files from the file system. Python 3.13+ changed the definition of `os.path.isabs` so that root-relative paths like `/windows/win.ini` on Windows are no longer considered absolute paths, resulting in a vulnerability in Gradio's logic for joining paths safely. This can be exploited by unauthenticated attackers to read arbitrary files from the Gradio server, even when Gradio is set up with authentication. Version 6.7 fixes the issue.
- CWE
- CWE-22
- CVSS base score
- 7.5
- Published
- 2026-02-27
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- Python 3.13+
- Fixed by upgrading
- Yes
Solution
Upgrade Gradio to version 6.7 or later.
Vulnerable code sample
import os
import sys
# This code represents the logic vulnerable to CVE-2026-28414.
# The vulnerability exists on Windows systems running Python 3.13+
# due to a behavioral change in `os.path.isabs`.
#
# To simulate the Python 3.13+ behavior on older versions for this PoC,
# we can mock `os.path.isabs` if not on Windows.
def _is_vulnerable_python_on_windows():
"""Checks for the specific vulnerable environment."""
# This is a hypothetical check for the described environment.
# We assume Python 3.13 is major=3, minor=13.
return sys.platform == 'win32' and sys.version_info >= (3, 13)
# If we are not in the vulnerable environment, we will mock the behavior
# of os.path.isabs for the purpose of this demonstration.
if not _is_vulnerable_python_on_windows():
print("INFO: Not running on Windows with Python 3.13+. Mocking vulnerable `os.path.isabs` behavior for demonstration.", file=sys.stderr)
_original_isabs = os.path.isabs
def _mocked_isabs_for_poc(path):
# On Python 3.13+/Windows, os.path.isabs('/drive/relative/path') is False.
# This is the core of the issue.
if isinstance(path, str) and (path.startswith('/') or path.startswith('\\')):
# A simple check: if it looks like a root-relative path but not a UNC path,
# simulate the vulnerable behavior by returning False.
if len(path) > 1 and path[1] not in (':', '\\', '/'):
return False
return _original_isabs(path)
os.path.isabs = _mocked_isabs_for_poc
def get_file_content_vulnerable(user_provided_path: str):
"""
This function simulates the vulnerable logic in Gradio prior to the fix.
It attempts to prevent access to absolute paths but fails for root-relative
paths on Windows with Python 3.13+.
"""
# This is a simulated 'safe' directory where user files are supposed to be stored.
base_dir = os.path.abspath("./safe_temp_dir")
# 1. The flawed security check.
# On a vulnerable system, `os.path.isabs('/windows/win.ini')` returns `False`.
# This allows the path to bypass the security check.
if os.path.isabs(user_provided_path):
print(f"[!] SECURITY CHECK: Blocked absolute path access: {user_provided_path}")
return None
# 2. The unsafe path joining.
# On Windows, `os.path.join('C:\\app\\safe_dir', '/windows/win.ini')`
# evaluates to 'C:/windows/win.ini', completely ignoring the base directory.
# Python normalizes the slashes, so the effective result is a path on the root of the current drive.
full_path = os.path.join(base_dir, user_provided_path)
print(f"[*] Attempting to access file at: {os.path.abspath(full_path)}")
# 3. Reading the file.
# If the path traversal was successful, this will read the arbitrary file.
try:
with open(full_path, 'r', encoding='utf-8', errors='ignore') as f:
# Return the first 200 characters for the PoC
return f.read(200)
except FileNotFoundError:
print(f"[!] File not found: {os.path.abspath(full_path)}")
return None
except Exception as e:
print(f"[!] An error occurred: {e}")
return None
if __name__ == '__main__':
# --- Demonstration ---
# Setup a simulated safe directory
if not os.path.exists("./safe_temp_dir"):
os.makedirs("./safe_temp_dir")
with open("./safe_temp_dir/legitimate_file.txt", "w") as f:
f.write("This is a safe and legitimate file.")
print("--- Demonstrating Vulnerability CVE-2026-28414 ---\n")
# --- Case 1: Legitimate file access (intended behavior) ---
print("1. Testing legitimate file access...")
content = get_file_content_vulnerable("legitimate_file.txt")
if content:
print(f" -> SUCCESS (intended): Read content: '{content}'\n")
# --- Case 2: Blocked absolute path (how it should work for all absolute paths) ---
# We find a common file, e.g., hosts file, and use its full absolute path.
# This path should be correctly identified as absolute and blocked.
print("2. Testing blocked absolute path...")
# This path is absolute and should be blocked by the `os.path.isabs` check.
# This works correctly in all versions.
absolute_path_to_block = os.path.join(os.environ.get("SystemRoot", "C:\\Windows"), "System32\\drivers\\etc\\hosts")
get_file_content_vulnerable(absolute_path_to_block)
print(" -> SUCCESS (intended): The check correctly blocked the absolute path.\n")
# --- Case 3: The Exploit (root-relative path traversal) ---
print("3. Testing the exploit with a root-relative path...")
# This path is NOT considered absolute by the vulnerable Python version on Windows.
# It bypasses the check, and `os.path.join` then resolves it from the drive root.
exploit_path = "/Windows/win.ini"
print(f" Payload: '{exploit_path}'")
print(f" `os.path.isabs('{exploit_path}')` returns: {os.path.isabs(exploit_path)}")
content = get_file_content_vulnerable(exploit_path)
if content:
print("\n -> VULNERABILITY EXPLOITED! Arbitrary file content was read:")
print("-" * 20)
print(content)
print("-" * 20)
else:
print("\n -> Exploit did not retrieve content (file may not exist at C:\\Windows\\win.ini or permissions error).")Patched code sample
import os
def _is_potentially_absolute_path(filepath: str) -> bool:
"""
Checks if a filepath is absolute or could be treated as absolute by os.path.join.
This function represents the fix for the vulnerability described in CVE-2026-28414,
where Gradio < 6.7 on Windows with Python 3.13+ was vulnerable to path traversal.
The vulnerability stems from Python 3.13+ changing `os.path.isabs` behavior on
Windows. Paths like '/windows/win.ini' are no longer considered absolute,
allowing them to bypass older security checks that relied solely on `os.path.isabs`.
However, `os.path.join` on Windows still treats such paths as absolute from the
current drive's root (e.g., C:\), ignoring any base directory and leading to
arbitrary file access.
This corrected function enhances the check by not only using `os.path.isabs()` but also
explicitly identifying paths that start with a slash on Windows, as these are
the root-relative paths that cause the traversal.
"""
if os.path.isabs(filepath):
return True
# FIX: On Windows, also check for root-relative paths that start with a
# slash. For example, `os.path.join("c:/some/dir", "/windows/system.ini")`
# results in "c:/windows/system.ini", effectively ignoring the base directory.
# This check prevents that behavior from being exploited.
if os.name == "nt" and filepath.startswith(("/", "\\")):
return True
return FalsePayload
/windows/win.ini
Cite this entry
@misc{vaitp:cve202628414,
title = {{Gradio path traversal on Win/Py3.13+ allows unauthenticated file read.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-28414},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-28414/}}
}
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 ::
