-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
os.path.normpath of relative path r".\C:\x" returns absolute path r"C:\x" on Windows, similar in pathlib #100162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I wouldn't hold up ".\C:\Windows\System32" as a shining example because it's an invalid path. Removing the leading "." component in this case doesn't break a path that would otherwise work. That said, removing the leading "." component leads to a couple of other issues that have been discussed previously. The first is the need to use a leading "." component in order to avoid ambiguity with a single-letter filename that contains named file streams. For example, ".\C:spam" refers to a data stream named "spam" in a file or directory named "C" in the current working directory. Without the leading "." component, "C:spam" is a file or directory named "spam" relative to the current working directory of drive "C:". (Each drive has its own working directory, which defaults to the root directory.) The other issue is that removing the leading "." component can change the meaning of a path of an executable that's passed to POSIX IIRC, neither of these issues has been seen as urgent enough to change the long-standing behavior |
It's worth noting that WinAPI >>> import ctypes
>>> winpath = ctypes.OleDLL('api-ms-win-core-path-l1-1-0')
>>> out_path = (ctypes.c_wchar * 1000)()
>>> winpath.PathCchCanonicalizeEx(out_path, 1000, r'.\C:\spam', 0)
0
>>> out_path.value
'C:\spam'
>>> winpath.PathCchCombineEx(out_path, 1000, r'.\C:\spam', 'eggs', 0)
0
>>> out_path.value
'C:\\spam\\eggs' On Windows, the internal functions |
Agreed, I don't think Python itself is vulnerable here, but our users may have written code that is exploitable by an error case becoming a legitimate case:
First immediate fix should be to update the docs to discourage calling I'm inclined to think that the best fix is for I can't come up with a reason to not do this on all platforms, especially given it's common code for all platforms, but maybe there's something I missed? |
We would also have to handle cases such as "spam/../C:eggs", which is a valid path for a file named "C" containing a data stream named "eggs" . This example should be normalized as I suggest the following change to the final step of the pure Python implementation:
This is a minimal change, which doesn't always preserve an initial "." component in the path argument. We could easily implement the latter as well by checking the initial value of |
Good point. Perhaps this check may also work?
Should be easy to tack onto the end of either approach, and will avoid changing anything if the path didn't change. There shouldn't be any scenarios where the first colon moves later in the path, and anywhere it stays at the same location we can ignore. |
Consider the valid path "foo/../spam/c:eggs", which normalizes as Consider the invalid path "foo/../c:/bar", which normalizes as Primarily, there's a serious issue if the initial path has no drive or root (i.e. Secondarily, there's a problem if the path intentionally begins with a "." component that gets normalized away. There's no difference if the path is opened, but it changes the intent of the path in a search context, such as >>> import os
>>> from win32api import SearchPath
>>> open(r'C:\Temp\test\spam.exe', 'w').close()
>>> os.getcwd()
'C:\\'
>>> SearchPath(r'C:\Temp', r'.\test\spam', '.exe')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pywintypes.error: (2, 'SearchPath', 'The system cannot find the file specified.')
>>> SearchPath(r'C:\Temp', r'test\spam', '.exe')
('C:\\Temp\\test\\spam.exe', 13) |
Thanks. So this leads us back to:
Both are potentially breaking changes, unfortunately. The second part is probably justifiable as a security fix, but I'm less sure about the first change. I don't like framing the fix in terms of the Python implementation, because it doesn't look anything like the native implementation anymore. Let's agree on behaviour in terms of the input value, and then we can implement that. |
os.path.normpath
normalizes "./" or "../" path elements to clean the path.As
os.path.normpath
doesn't consider a case where the normalized path starts with a drive letter, a relative path with a drive letter-like path element will be normalized to an absolute path. This behavior can result in a path traversal, depending on the implementation.The minimal snippet to reproduce this behavior is the following:
This snippet will return
"C:\Windows\System32"
, which is an absolute path on Windows. (tested with Python 3.11.1 on Windows)This vulnerability is similar to CVE-2022-29804 of Go.
as reported to security@ by RyotaK on 2022-12-09.
Golang solved their issue in golang/go#52476 which may be helpful to look at.
The Python Security Response Team agreed that this issue did not require an embargo.
It looks like
pathlib
probably also has issues in this area:The text was updated successfully, but these errors were encountered: