Tornado StaticFileHandler Symlink Path Traversal
A path traversal vulnerability in Tornado's StaticFileHandler allows unauthenticated remote attackers to read arbitrary files via symbolic links within the static root directory.
Tornado versions 6.5.8 and earlier contain a path traversal vulnerability in the StaticFileHandler component that permits unauthenticated attackers to read arbitrary files from the filesystem. The vulnerability exists because get_absolute_path and validate_absolute_path utilize os.path.abspath() to normalize requested paths and validate them against the configured static root. While os.path.abspath() correctly normalizes directory traversal sequences like .., it fails to resolve symbolic links. Subsequent file access operations (such as os.path.isfile()) resolve these symlinks to their actual targets.
If an application serves a static directory containing symlinks - often introduced by build pipelines, web frameworks, or improper user-uploaded file handling - an attacker can traverse outside the intended static root by requesting a path that resolves through a symlink to sensitive files like configuration credentials, private keys, or system files. Defending organizations must ensure that StaticFileHandler is not used to serve directories containing untrusted or externally-linked content, or upgrade to a version incorporating os.path.realpath() for validation.
Attack Chain
- Attacker identifies a target application utilizing the
tornado.web.StaticFileHandler. - Attacker confirms the existence of a symbolic link within the application's static directory pointing to a sensitive file (e.g.,
/etc/passwdor/app/config.json). - Attacker sends a GET request to the Tornado server targeting the identified symlink (e.g.,
GET /static/symlink_name HTTP/1.1). - The
StaticFileHandlerinvokesvalidate_absolute_path, which usesos.path.abspath()to check if the path starts with the configured static root. os.path.abspath()considers the path valid because it resides within the static directory structure as a string, ignoring that it is a symlink.- The application proceeds to the file access stage where
os.path.isfile()and subsequent read operations resolve the symlink target. - The server reads the file content from the target location and returns the sensitive data to the attacker in the HTTP response body.
Impact
Successful exploitation allows unauthenticated remote attackers to read any file readable by the user account running the Tornado application process. Depending on the environment, this typically includes application configuration secrets, database connection strings, TLS private keys, SSH keys, source code, and sensitive system files like /etc/shadow. This vulnerability impacts any deployment where the static root contains symlinks, which is a common scenario in modern development environments using Docker volume mounts, webpack-based build tools, or npm link.
Recommendation
- Upgrade Tornado to a version that implements
os.path.realpath()for directory validation to resolve and restrict symlinks to the static root. - Identify and audit all directories served by
StaticFileHandlerfor the presence of symbolic links usingfind /path/to/static -type l. - Implement file system permissions that restrict the Tornado process user from accessing sensitive files outside of its intended scope as a defense-in-depth measure.
- Monitor web server logs for suspicious requests to files typically not served as static assets, such as files ending in
.conf,.key,.pem, or system configuration files.
Immediate actions
Audit static directories for symlinks and restrict serving them via web server configuration.
Threat Hunt
Request patterns for sensitive file extensions in static root paths.
Data: Web server access logs
Mitigations
Upgrade Tornado to the patched version.
Tornado (<= 6.5.8)