GHSA-C2M8-H5V5-343R

Vulnerability from github – Published: 2026-09-30 23:48 – Updated: 2026-09-30 23:48
VLAI
Summary
Tornado: StaticFileHandler follows symlinks outside static root (path traversal)
Details

Summary

StaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server's filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root—or exploit one that already exists—can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets.

Details

The vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in tornado/web.py: - get_absolute_path uses os.path.abspath() to resolve the requested path: - validate_absolute_path uses the same os.path.abspath() on the root, then performs a string prefix check:

os.path.abspath() normalises . and .. segments but does not resolve symbolic links. As a result, a path like /var/www/static/link passes the startswith("/var/www/static/") check regardless of where link actually points.

Immediately after, os.path.exists() and os.path.isfile() do follow symlinks, so the file they ultimately open is the symlink's target. The fix would be to replace os.path.abspath() with os.path.realpath() in both methods, so the resolved real path of the symlink target is validated against the root, not just its string representation inside the static directory.

PoC

Complete instructions, including specific configuration details, to reproduce the vulnerability. Prerequisites: Python 3.x, Tornado installed. 1. Create the environment

mkdir -p /tmp/static
echo "DB_PASSWORD=s3cr3t" > /tmp/secret.conf   
ln -s /tmp/secret.conf /tmp/static/config.conf 
  1. Minimal vulnerable server (server.py):
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()
  1. Exploit: curl http://localhost:8888/static/config.conf

Impact

Any application using StaticFileHandler is potentially affected if: - the static directory contains symlinks pointing outside it (common with build tooling), or - the application allows file uploads into the static directory without stripping symlinks.

An unauthenticated remote attacker can read any file readable by the process user: application secrets, private TLS keys, database credentials, /etc/shadow, SSH keys, or source code (depending on the process's filesystem permissions).

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.5.8"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "tornado"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.5.9"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-59"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-30T23:48:55Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\nStaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server\u0027s filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root\u2014or exploit one that already exists\u2014can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets.\n\n\n\n### Details\nThe vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in [tornado/web.py](vscode-webview://1grdaaa6v64itrol06qu70pq3d1b9h9im5c3npvvc9snaprc1q9f/tornado/web.py):\n- [get_absolute_path](https://github.com/tornadoweb/tornado/blob/0ca3c5f8279d402b245718d16522bc18a8f5d958/tornado/web.py#L2932) uses `os.path.abspath()` to resolve the requested path:\n- [validate_absolute_path](https://github.com/tornadoweb/tornado/blob/0ca3c5f8279d402b245718d16522bc18a8f5d958/tornado/web.py#L2935) uses the same `os.path.abspath()` on the root, then performs a string prefix check:\n\n`os.path.abspath()` normalises . and .. segments but does not resolve symbolic links. As a result, a path like `/var/www/static/link` passes the `startswith(\"/var/www/static/\")` check regardless of where `link` actually points.\n\nImmediately after, `os.path.exists()` and `os.path.isfile()`  **do follow symlinks**, so the file they ultimately open is the symlink\u0027s target. The fix would be to replace os.path.abspath() with os.path.realpath() in both methods, so the resolved real path of the symlink target is validated against the root, not just its string representation inside the static directory.\n\n### PoC\n_Complete instructions, including specific configuration details, to reproduce the vulnerability._\nPrerequisites: Python 3.x, Tornado installed.\n1. Create the environment\n```\nmkdir -p /tmp/static\necho \"DB_PASSWORD=s3cr3t\" \u003e /tmp/secret.conf   \nln -s /tmp/secret.conf /tmp/static/config.conf \n```\n2. Minimal vulnerable server (server.py):\n```\nimport tornado.web, tornado.ioloop\n\napp = tornado.web.Application([\n    (r\"/static/(.*)\", tornado.web.StaticFileHandler, {\"path\": \"/tmp/static\"}),\n])\napp.listen(8888)\ntornado.ioloop.IOLoop.current().start()\n```\n3. Exploit:\n`curl http://localhost:8888/static/config.conf`\n\n### Impact\nAny application using StaticFileHandler is potentially affected if:\n- the static directory contains symlinks pointing outside it (common with build tooling), or\n- the application allows file uploads into the static directory without stripping symlinks.\n\nAn unauthenticated remote attacker can read any file readable by the process user: application secrets, private TLS keys, database credentials, /etc/shadow, SSH keys, or source code (depending on the process\u0027s filesystem permissions).",
  "id": "GHSA-c2m8-h5v5-343r",
  "modified": "2026-09-30T23:48:55Z",
  "published": "2026-09-30T23:48:55Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/security/advisories/GHSA-c2m8-h5v5-343r"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/pull/3719"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/commit/437ab5f76565403b3403438d2e555d11f8128d32"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/commit/f9f50b8bab265a2abf9f2415a3d2da6264a5450b"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/tornadoweb/tornado"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/releases/tag/v6.5.9"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Tornado: StaticFileHandler follows symlinks outside static root (path traversal)"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…