CVE-2023-41105 - Path Traversal Risk in Python 3.11’s os.path.normpath()—What Developers Must Know

CVE-2023-41105 exposes a subtle but important flaw discovered in Python 3.11 through 3.11.4, specifically in the os.path.normpath() function. This seemingly innocent function, responsible for cleaning and simplifying file system paths, handles null bytes ('\') in a way that can open your application up to security risks—especially if you rely on filename checks. This article walks through the vulnerability with clear examples, explains exploitation scenarios, and links to authoritative references for further reading.

What Is the Issue?

Up to Python 3.10.x, passing a string containing a null byte ('\') as a path to many core functions would raise an error—a long-standing security measure because null bytes can truncate strings at the OS level, leading to unexpected behavior.

However, in Python 3.11–3.11.4, os.path.normpath() truncates the given path at the first occurrence of '\' without raising an exception. This means a path like "dangerous\evil.txt" will silently become "dangerous".

If your code does security validation or filtering after normalizing a path, this truncation can let attackers sneak dangerous filenames past your checks.

Example: Code Snippet Vulnerable to CVE-2023-41105

Let’s walk through a sample Python program. Suppose you are writing a web app that takes user uploads, and you want to reject risky filenames (like anything with a .. path sequence).

import os

def is_safe_path(path):
    # Normalize path and check if it's safe
    normalized = os.path.normpath(path)
    # Disallow parent traversal
    if '..' in normalized:
        return False
    # Disallow weird characters
    if '\' in normalized:
        return False  # This check fails in Python 3.11+
    return True

filename = "user_uploads/../secret\.png"
print(is_safe_path(filename))

In Python 3.11–3.11.4

- The input is truncated at \, becoming "user_uploads/../secret".

Why Does This Matter?

Many file-handling applications check file names *after* normalization. Attackers may exploit this flaw by inserting null bytes in strategic positions, causing your security check to misfire.

Let’s say you block file extensions like .exe. If the filename is "photo.jpg\.exe", it passes your logic, but on some file systems or secondary software, the file with that name might be treated differently, causing security confusion or even an outright bypass.

Attacker uploads a filename with a null byte

- "../../etc/passwd\.sneaky"

Your app normalizes it

- os.path.normpath('../../etc/passwd\.sneaky')'../../etc/passwd'
3. You check the normalized string for forbidden patterns, but logic fails because "\.sneaky" vanishes.

Result

- Access checks, extension filtering, or protections may not work, letting the attacker create files where they shouldn’t.

Don’t rely on os.path.normpath() to handle null bytes.

- Manually reject any input containing \ before path normalization, as this will work regardless of Python version.

Safer function

def is_safe_path_fixed(path):
    if '\' in path:
        return False  # Block null bytes immediately
    normalized = os.path.normpath(path)
    if '..' in normalized:
        return False
    return True

Upgrade If Possible

- Python 3.11.5 and future releases will fix this issue by raising exceptions on null byte usage again. Always use the latest Python patch release.

Original References

- Python Security Advisory - CVE-2023-41105
- Python bug tracker issue #10849
- Release notes for Python 3.11.5
- os.path.normpath() documentation

Conclusion

CVE-2023-41105 reminds us that language upgrades bring subtle security changes. Always validate file paths before passing them to normalization—and don’t let legacy habits from earlier Python releases mislead your security posture. Patch your interpreters, review your code, and stay alert for “boring” functions causing surprising problems!

If you think your application could be affected, audit your path handling now—before an attacker does it for you.


For more exclusive guides like this, follow our Security Watch series!

Timeline

Published on: 08/23/2023 07:15:00 UTC
Last modified on: 09/01/2023 13:37:00 UTC