Oct 1, 2026·4 min read·5 visits
Tornado's StaticFileHandler prior to 6.5.9 is vulnerable to path traversal and arbitrary file disclosure. By performing lexical validation instead of physical path resolution, the application serves files targeted by symbolic links that point outside the static root.
A path traversal and arbitrary file disclosure vulnerability exists in Tornado's StaticFileHandler. In versions prior to 6.5.9, the handler follows symbolic links that point outside of the configured root static directory. This behavior occurs because the handler performs lexical path validation rather than physical filesystem resolution, allowing unauthenticated remote attackers to read arbitrary files if they can access or control symbolic links within the served static root.
The affected component is tornado.web.StaticFileHandler, which serves static files from a configured root directory in web applications. It exposes an attack surface by receiving client-controlled path inputs and resolving them against the local filesystem.
The vulnerability is classified as CWE-59 (Improper Link Resolution Before File Access) and CWE-22 (Path Traversal). In vulnerable configurations, if an attacker can access or create a symbolic link inside the configured static root directory, they can traverse boundaries. This allows unauthorized retrieval of arbitrary files from the server's local filesystem.
The core of the vulnerability lies in the use of lexical resolution instead of physical resolution during path validation. The affected handler validated the absolute path using os.path.abspath, which performs pure string manipulation. Lexical resolution does not query the filesystem to determine whether path elements are symbolic links.
Because os.path.abspath validated that the path was lexically inside the configured root directory, the boundary check succeeded. However, when the file was subsequently opened, the operating system's filesystem resolved the physical symbolic link. This redirection allowed access to files located entirely outside the designated static root directory.
Furthermore, deferring physical resolution created a potential "existence oracle" vulnerability. If physical path resolution occurs after checking if a directory exists, requesting an out-of-bounds directory could cause a redirect (HTTP 301). Requesting a non-existent directory would return a standard error (HTTP 403 or 404), revealing the host layout to the attacker.
The fix in version 6.5.9 addresses the bug by introducing an explicit physical resolution step during validation. The StaticFileHandler was updated with a new initializer argument named allowed_symlink_directory. This argument defaults to the configured base path but can be expanded manually.
The validation workflow first runs the lexical check, which remains a low-overhead defense against standard relative path traversal attempts. Following this, the method calls _resolve_symlink_target to obtain the physical canonical path using os.path.realpath. This canonical path is verified to ensure it begins with the allowed symlink directory path.
Additionally, the patch replaces multiple filesystem checks with a single-pass os.stat call. This optimization binds the validated path details to a static local state. By preventing multiple lookups during header generation, the code mitigates potential Time of Check to Time of Use (TOCTOU) race conditions.
Exploitation of this vulnerability requires that a symbolic link pointing outside the static root directory exists inside the static folder. This condition typically occurs in shared multi-tenant filesystems, repositories containing checked-in symlinks, or applications allowing custom file uploads.
An attacker executes the payload by requesting the symlink or a subpath of the symlinked directory over HTTP. The StaticFileHandler receives the path segment and matches it against its routing table. Because the requested URI does not contain direct lexical traversal characters like .., the initial validation steps are bypassed.
When the request finishes processing, the physical file is read and returned to the client. This exposes sensitive system files such as database configurations, application source code, or system credentials.
The primary remediation strategy is upgrading the tornado package to version 6.5.9 or higher. This upgrade implements physical symlink target validation by default. If symlinks are required, administrators must configure allowed_symlink_directory explicitly to safe boundaries.
Operational workarounds include structuring filesystems to disallow symbolic links in directories handled by the static handler. Security teams should also filter user-uploaded archives to block the extraction of symbolic links. Running the application inside an isolated read-only container also restricts filesystem access.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Tornado Tornadoweb | < 6.5.9 | 6.5.9 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-59, CWE-22 |
| Attack Vector | Network |
| CVSS Score | 7.5 (High) |
| Impact | Arbitrary File Disclosure |
| Exploit Status | PoC / Functional (via unit tests) |
| KEV Status | Not listed |
The application attempts to access a file based on a filename, but it does not properly validate whether that file is a symbolic link or shortcut that redirects the access to an unintended resource.
An improper neutralization of input during web page generation (CWE-79) vulnerability exists in the server-side rendering JSX engine (hono/jsx) of the Hono web framework prior to version 4.13.7. The flaw enables unauthenticated remote attackers to execute arbitrary JavaScript in the victim's browser context by supplying unescaped HTML characters into user-controlled fields rendered within specific boundary components, context providers, or direct server-side utilities.
A path traversal vulnerability exists in GitPython when handling submodule updates recursively. If an attacker crafts a malicious repository with traversed paths or symbolic links in the submodule configuration, they can execute arbitrary file writes outside the parent repository's working directory. This can lead to system configuration modifications or arbitrary code execution.
A critical uncontrolled resource consumption vulnerability exists in the Tornado web server's libcurl-based HTTP client (CurlAsyncHTTPClient). When processing highly compressed responses with response decompression enabled, the client experiences unbounded memory growth. This leads to host memory exhaustion and denial of service via application crashes.
An uncontrolled resource consumption vulnerability in Tornado's HTTP query-string parser allows remote, unauthenticated attackers to trigger CPU exhaustion and block the single-threaded event loop via crafted request URIs containing large numbers of parameters.
PyJWT versions 2.11.0 through 2.13.0 suffer from a state pollution vulnerability in the `_merge_options` method. When an application passes a mutable configuration mapping with signature verification disabled, the library modifies the object in-place. If this same dictionary is reused for subsequent verified decode operations, standard claim verifications (such as expiration, audience, and issuer validation) are silently bypassed.
A protocol validation vulnerability exists in Fastify before version 5.12.5. When serving requests over HTTP/2, Fastify unconditionally injects the forbidden Transfer-Encoding header when response trailers are used, triggering an uncaught exception in Node.js and crashing the process.