Mar 12, 2026·6 min read·2 visits
TinaCMS development servers < 2.1.7 are vulnerable to unauthenticated arbitrary file writes via a path traversal flaw in the media upload handler. Attackers can leverage this to overwrite source files or configuration data, potentially achieving Remote Code Execution (RCE).
A high-severity path traversal vulnerability in the TinaCMS development server prior to version 2.1.7 allows unauthenticated attackers to write arbitrary files to the host filesystem. The vulnerability exists in the media upload handler, which improperly sanitizes user-supplied file paths.
TinaCMS is a headless content management system that relies on a local development server to manage content and media assets. The vulnerability resides within the media upload handler of this development server, specifically affecting versions prior to 2.1.7. This component is responsible for accepting user-uploaded files and writing them to a designated media directory.
The core issue is a CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) vulnerability. The server processes incoming media uploads without adequately validating the destination file path provided in the request. By supplying a crafted path containing specific character sequences, an attacker can escape the intended directory boundaries.
Exploitation of this vulnerability requires network access to the development server, which typically listens on port 4001. No authentication is necessary to interact with the vulnerable endpoint. Successful exploitation results in arbitrary file write capabilities, directly impacting system integrity and availability.
The vulnerability stems from the insecure use of the Node.js path.join() utility within the media.ts handler. When a file upload request is received, the server extracts the user-controlled path parameter and concatenates it with the configured base media directory.
The path.join() function standardizes the resulting path by resolving . and .. segments. However, it does not enforce any boundary constraints. If a user supplies a path starting with multiple ../ sequences, path.join() will dutifully resolve the path upwards, effectively escaping the root media directory.
The application lacks a subsequent validation step to verify that the final, resolved path remains a child of the intended mediaRoot. Without a strict prefix check using path.resolve(), the application proceeds to execute file system operations at the attacker-specified location.
Because the process blindly trusts the concatenated path, any file write operation initiated by the upload handler occurs exactly where the attacker dictates, constrained only by the operating system permissions of the Node.js process.
In the vulnerable implementation, the file upload handler accepts a payload containing the file data and a destination path. The server processes these inputs directly into a file system write operation.
// Conceptual vulnerable implementation in media.ts
const baseMediaDir = config.mediaRoot;
const userSuppliedPath = request.body.path;
// path.join does not restrict upward traversal
const uploadPath = path.join(baseMediaDir, userSuppliedPath);
// File is written to the escaped path
fs.writeFileSync(uploadPath, request.files.file.data);The patched implementation introduces a mandatory boundary check. It resolves both the base directory and the target upload path to their absolute forms using path.resolve().
// Conceptual patched implementation
const baseMediaDir = path.resolve(config.mediaRoot);
const userSuppliedPath = request.body.path;
// Resolve the final absolute path
const resolvedUploadPath = path.resolve(baseMediaDir, userSuppliedPath);
// Enforce boundary confinement
if (!resolvedUploadPath.startsWith(baseMediaDir)) {
throw new Error("Path traversal detected");
}
fs.writeFileSync(resolvedUploadPath, request.files.file.data);This remediation ensures that regardless of the input provided, the target file path must strictly begin with the absolute path of the authorized media directory. If an attacker attempts to traverse upwards, the startsWith condition fails, and the operation aborts securely.
Exploitation begins with the attacker identifying an accessible TinaCMS development server. While development servers are typically bound to the local loopback interface, misconfigurations or exposed container ports can make them reachable over a network. The attacker targets the media upload API endpoint, commonly located at routes such as /api/proxy/media.
The attacker crafts a multipart/form-data HTTP POST request. Within this request, the filename or path parameter is manipulated to include directory traversal payloads. A typical payload involves multiple ../ sequences followed by the target file path, such as ../../../../app/index.js.
POST /api/proxy/media HTTP/1.1
Host: target-server:4001
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="path"
../../../../app/index.js
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="payload.js"
Content-Type: application/javascript
console.log("Execution achieved");
// Malicious Node.js payload here
------WebKitFormBoundary--Upon processing the request, the server writes the attached file content to the traversed path. If the attacker successfully overwrites an application source file or configuration script, the payload executes during the next server restart or hot-reload event, finalizing the attack.
The immediate impact of CVE-2026-28791 is an arbitrary file write condition. An unauthenticated network attacker can create new files or overwrite existing files anywhere on the host system, provided the Node.js process has the requisite write permissions. This directly satisfies the High Integrity impact metric in the CVSS vector.
Overwriting critical application files creates a reliable path to Remote Code Execution (RCE). Development environments frequently utilize hot-reloading mechanisms (such as nodemon or webpack dev server). Overwriting a tracked source file immediately triggers a reload, executing the attacker's injected code within the context of the server process.
Furthermore, the availability of the system is highly impacted. An attacker can intentionally overwrite necessary system configuration files or application binaries with garbage data, rendering the development server or the host machine inoperable. This fulfills the High Availability impact metric.
The risk is amplified by the nature of development environments, which often contain sensitive assets. Attackers achieving code execution can extract .env files containing production API keys, database credentials, or access tokens, facilitating lateral movement into production infrastructure.
The primary and most effective remediation is to update TinaCMS to version 2.1.7 or later. The patched versions implement strict path resolution and boundary checking, neutralizing the traversal vector entirely. Administrators should update dependencies in their package.json and rebuild the environment.
If immediate patching is not feasible, organizations must ensure network isolation. Development servers must only bind to the local loopback interface (127.0.0.1 or localhost). Verify that container orchestration configurations (like Docker Compose) do not inadvertently expose port 4001 to external networks or untrusted internal subnets.
As a defense-in-depth measure, enforce the principle of least privilege. The user account executing the TinaCMS development server should only possess write access to the specific media directories required for normal operation. Restricting write permissions on application source files and sensitive system directories significantly limits the blast radius of a successful arbitrary file write attack.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
TinaCMS TinaCMS | < 2.1.7 | 2.1.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS v3.1 Score | 7.4 |
| Impact | Arbitrary File Write / RCE |
| Exploit Status | None |
| CISA KEV | No |
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')