Tornado StaticFileHandler, Symbolic Link Arbitrary File Read, CVE: N/A -DC-Oct2026-2690

Listen to this Post

Tornado’s StaticFileHandler serves files under a configured static root.
It joins the root with the requested URI path.

It calls os.path.abspath() on the root.

It calls os.path.abspath() on the candidate path.

It checks that the candidate starts with the root.

os.path.abspath() normalises “.” and “..” segments.

os.path.abspath() does not resolve symbolic links.

A symlink inside the static root can point outside the root.
The string path still starts with the static root prefix.

The prefix check therefore passes.

StaticFileHandler then calls os.path.exists().

StaticFileHandler also calls os.path.isfile().

Both functions follow symbolic links.

The file opened is the symlink target.

The target can be any file readable by the process user.

An unauthenticated attacker can request the symlink path.

The request can return /etc/passwd, private keys, configuration files, or secrets.

The vulnerability is in tornado/web.py.

It affects path validation and file opening.

Any application using StaticFileHandler may be affected.

The static directory may contain symlinks from build tooling.
Examples include npm link, webpack, Docker volume mounts, CDN sync tools.
The application may allow uploads into the static directory.

Uploads without symlink stripping can create malicious links.

The fix is to replace os.path.abspath() with os.path.realpath().

The resolved real path must be validated against the static root.

Both validation methods should use realpath.

This prevents symlink targets outside the root from passing the check.
No CVE identifier was provided in the source .
Severity and affected versions were not stated in the source .

DailyCVE Form:

Platform: Tornado StaticFileHandler
Version: Unspecified Tornado versions
Vulnerability : Symlink file read
Severity: Not stated
date: Not stated

Prediction: Unknown patch date

(end of form)

What Undercode Say:

Analytics

mkdir -p /tmp/static
echo "DB_PASSWORD=s3cr3t" > /tmp/secret.conf
ln -s /tmp/secret.conf /tmp/static/config.conf
import tornado.web, tornado.ioloop
app = tornado.web.Application([
(r"/static/(.)", tornado.web.StaticFileHandler, {"path": "/tmp/static"}),
])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
curl http://localhost:8888/static/config.conf

How Exploit: (Educational Purposes!)

mkdir -p /tmp/static
echo "DB_PASSWORD=s3cr3t" > /tmp/secret.conf
ln -s /tmp/secret.conf /tmp/static/config.conf
python server.py
curl http://localhost:8888/static/config.conf

Protection: from this CVE

os.path.realpath()
startswith(realpath(root))
Reject symlinks
Strip symlinks on upload
Run process least privilege

Impact:

Unauthenticated remote attacker reads arbitrary files readable by process user.

/etc/passwd

private keys

configuration files

application secrets

/etc/shadow

SSH keys

source code

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top