CVE-2023-40587 (published 2023-08-25T21:15Z)

Pyramid is an open source Python web framework. A path traversal vulnerability in Pyramid versions 2.0.0 and 2.0.1 impacts users of Python 3.11 that are using a Pyramid static view with a full filesystem path and have a `index.html` file that is located exactly one directory above the location of the static view's file system path. No further path traversal exists, and the only file that could be disclosed accidentally is `index.html`. Pyramid version 2.0.2 rejects any path that contains a null-byte out of caution. While valid in directory/file names, we would strongly consider it a mistake to use null-bytes in naming files/directories. Secondly, Python 3.11, and 3.12 has fixed the underlying issue in `os.path.normpath` to no longer truncate on the first `0x00` found, returning the behavior to pre-3.11 Python, un an as of yet unreleased version. Fixes will be available in:Python 3.12.0rc2 and 3.11.5. Some workarounds are available. Use a version of Python 3 that is not affected, downgrade to Python 3.10 series temporarily, or wait until Python 3.11.5 is released and upgrade to the latest version of Python 3.11 series.

CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/Pylons/pyramid
commit_details: repo_url: https://github.com/Pylons/pyramid
author: Tres Seaver
author_date: 2023-08-21 14:43:12-04:00
msg: fix: reject NUL character as path element

See: https://github.com/Pylons/pyramid/security/advisories/GHSA-j8g2-6fc7-q8f8
repository: repo_url: https://github.com/Pylons/pyramid
repo_name: Pylons/pyramid
description: Pyramid - A Python web framework
repo_language: Python
owner: Pylons
file_changes: filename: static.py
change_type: ModificationType.MODIFY
diff: @@ -260,12 +260,12 @@ def _add_vary(response, option):
     response.vary = vary
 
 
-_seps = {'/', os.sep}
+_invalid_element_chars = {'/', os.sep, '\x00'}
 
 
-def _contains_slash(item):
-    for sep in _seps:
-        if sep in item:
+def _contains_invalid_element_char(item):
+    for invalid_element_char in _invalid_element_chars:
+        if invalid_element_char in item:
             return True
 
 
@@ -279,7 +279,7 @@ def _secure_path(path_tuple):
         # unless someone screws up the traversal_path code
         # (request.subpath is computed via traversal_path too)
         return None
-    if any([_contains_slash(item) for item in path_tuple]):
+    if any([_contains_invalid_element_char(item) for item in path_tuple]):
         return None
     encoded = '/'.join(path_tuple)  # will be unicode
     return encoded

programming_language: Python
filename: index.html
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+<h1>DON'T GO HERE</h1>

programming_language: SQL
filename: test_static.py
change_type: ModificationType.MODIFY
diff: @@ -104,6 +104,19 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
 
         self.assertRaises(HTTPNotFound, inst, context, request)
 
+    def test_oob_nul_char(self):
+        import os
+
+        inst = self._makeOne(f'{os.getcwd()}/tests/fixtures/static')
+        dds = '..\x00/'
+        request = self._makeRequest(
+            {'PATH_INFO': f'/{dds}'}
+        )
+        context = DummyContext()
+        from pyramid.httpexceptions import HTTPNotFound
+
+        self.assertRaises(HTTPNotFound, inst, context, request)
+
     def test_resource_doesnt_exist(self):
         inst = self._makeOne('tests:fixtures/static')
         request = self._makeRequest({'PATH_INFO': '/notthere'})

programming_language: Python