Jun 24, 2026·6 min read·24 visits
An input validation flaw during provider extraction in OpenTofu allows pre-seeded symbolic links to redirect file writes to arbitrary paths on the host system, enabling arbitrary file write outside the workspace.
A UNIX symbolic link following vulnerability exists in the provider cache installation mechanism of OpenTofu. This flaw allows an attacker with control over the repository files to write files outside of the intended workspace boundary during initialization.
OpenTofu downloads provider binaries and extracts them to a local cache directory, typically inside .terraform/providers/, within the workspace root directory. This cache folder serves as a local repository for plugins required by the configuration. During the initialization phase initiated by tofu init, the application fetches the required providers, unpacks their compressed archives, and structures them in designated directory paths.
Prior to version 1.10.10, 1.11.7, and 1.12.0, the package installation process did not properly validate whether pre-existing cache directories were actual physical folders or symbolic links. This omission exposes the workspace to directory traversal and out-of-bounds writes if untrusted repositories are loaded into the utility.
This flaw is classified under CWE-61 (UNIX Symbolic Link Following). If an attacker can construct a workspace containing pre-created symbolic links targeting system-level directories and coerce an operator or CI/CD pipeline into running OpenTofu, the application will follow the link and write provider files into the target directories.
The root cause of this vulnerability lies in the use of the os.Stat function inside the package extraction routines in internal/getproviders/package_location_local_archive.go. When verifying if the destination provider package directory targetDir is already present, OpenTofu called os.Stat(targetDir) to confirm its status.
The standard Go implementation of os.Stat automatically follows symbolic links to their final target on the operating system. If a symbolic link is placed at targetDir pointing to /usr/local/bin, os.Stat retrieves the properties of /usr/local/bin instead of evaluating the symlink itself. Because the target directory exists, os.Stat returns a success code.
Furthermore, the application lacked logic to delete the pre-existing directory structure if the local cache hashes did not match the newly downloaded package contents. Because the application assumed targetDir was a standard folder, the archive extraction routine proceeded to write files into the destination path, which resolved to the symbolic link's destination, executing a write operation outside of the workspace directory.
The vulnerability was corrected by transitioning filesystem evaluation from os.Stat to os.Lstat and implementing a strict verification validation step before proceeding with package decompression. Unlike os.Stat, os.Lstat queries the metadata of the symbolic link itself without resolving its destination.
Below is the comparison between the vulnerable and patched check sequences in internal/getproviders/package_location_local_archive.go:
// VULNERABLE LOGIC FLOW
if _, err := os.Stat(targetDir); err == nil {
// Stat resolves the link target
targetHash, targetErr := PackageHashV1(PackageLocalDir(targetDir))
fileHash, fileErr := PackageHashV1(meta.Location)
if targetHash == fileHash && fileErr == nil && targetErr == nil {
// Skipping occurs only if target hashes match exactly.
// If they mismatch, extraction continues into targetDir.
return authResult, nil
}
}// PATCHED LOGIC FLOW
if info, err := os.Lstat(targetDir); err == nil {
log.Printf("[TRACE] There's already a directory entry at %s, so we'll check if it matches our expectations", targetDir)
targetHash, targetErr := PackageHashV1(PackageLocalDir(targetDir))
// Verify if it is a real physical directory and is entirely empty
isEmptyDir := info.IsDir() && targetHash == emptyPackageHashV1
if !isEmptyDir {
fileHash, fileErr := PackageHashV1(meta.Location)
var err error
if fileErr != nil {
err = fmt.Errorf("failed to calculate checksum for temporary copy of provider package")
} else if targetErr != nil {
err = fmt.Errorf("failed to calculate checksum for existing cached provider package")
} else if targetHash != fileHash {
// Prevent file write and return error instead of silently overwriting
err = fmt.Errorf("existing cached package at %s does not match the content of the downloaded package", targetDir)
}
if err != nil {
tracing.SetSpanError(span, err)
return authResult, err
}
}
}This implementation closes the vulnerability by verifying that the existing file path is a real directory (info.IsDir() returns false for a symlink) and that the directory is empty. If these conditions are not met, and the package hash does not match, OpenTofu raises an error and halts execution, preventing file extraction.
To exploit this flaw, an attacker must commit a malicious directory structure to a version control system and wait for an operator or automated agent to run tofu init on the repository.
The attack path proceeds as follows:
By creating a symlink in the local provider path pointing to a system path (such as /usr/local/bin), the attacker forces the archiver to unpack the downloaded provider binary outside of the workspace bounds during tofu init. If the process runs with administrative privileges, this leads to system modification or local execution of arbitrary binaries.
The security impact of GHSA-WCMJ-X466-56MM is classified as Medium, calculated with a CVSS base score of 6.1. The primary consequence is the write-only arbitrary file write capability, which allows an attacker to drop binaries or scripts outside the workspace directory structure.
While the flaw does not allow direct data retrieval or file reading, writing to operational paths on the filesystem is a well-established vector for privilege escalation. In continuous integration and deployment (CI/CD) environments, runners often execute processes under permissions that allow writing to global folders or executing scheduled cron tasks. If an attacker writes a custom executable script into /etc/cron.d/ or /usr/local/bin/, they can transition the arbitrary file write into full system compromise.
The primary vector is automated pull request workflows. A hostile external contributor could submit a branch containing a pre-seeded symbolic link. If the CI runner automatically triggers initialization on untrusted pull requests, the host machine executing the action is compromised.
To remediate this issue, all installations of OpenTofu must be updated to fixed versions. Supported branches have received backported updates. Users should migrate immediately to 1.10.10, 1.11.7, or 1.12.0 depending on their current release lifecycle.
If immediate software upgrade is not possible, operators can employ operational mitigations to prevent exploitation inside build pipelines:
rm -rf .terraform before running initialization commands on untrusted source codes.find . -path "*/.terraform/providers/*" -type lCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
github.com/opentofu/opentofu OpenTofu | < 1.10.10 | 1.10.10 |
github.com/opentofu/opentofu OpenTofu | >= 1.11.0, < 1.11.7 | 1.11.7 |
github.com/opentofu/opentofu OpenTofu | >= 1.12.0-alpha1, < 1.12.0 | 1.12.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-61 |
| Attack Vector | Network |
| CVSS v3.1 | 6.1 (Medium) |
| Impact | Arbitrary File Write |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The application performs file operations on a target that can be a symbolic link, but it does not check if the symlink points outside the intended directory.
A security feature bypass vulnerability in the Falco k8saudit plugin (and its cloud-specific variants) allowed privileged workloads to run undetected. This bypass occurred because the plugin's default extraction logic and rules only evaluated standard containers, completely omitting initContainers and ephemeralContainers.
nginx-ignition is a web-based user interface for managing the Nginx web server. In versions 2.33.0 through 2.35.0, the application is vulnerable to an improper authentication flaw (CWE-287) in its Multi-Factor Authentication (MFA) implementation. The stateless validation of Time-Based One-Time Passwords (TOTP) allows an attacker to reuse a captured, active verification code multiple times within the standard 30-second validity window, successfully bypassing secondary authentication checks if primary credentials are known.
A vulnerability exists in the i18n middleware of nginx-ignition, enabling CPU amplification attacks. By transmitting a crafted Accept-Language header containing malformed tags separated by underscores, an unauthenticated remote attacker can bypass the length-guard threshold of the underlying Go parsing library. Normalization of underscores to hyphens occurs after the initial validation checks, forcing the parser into expensive quadratic-time loops that consume 100% of available CPU resources. This leads to a complete denial of service for the administrative API and potentially degrades the availability of the hosting system. This vulnerability has been resolved in version 2.40.1.
Nginx Ignition prior to version 2.41.1 contains a Time-of-Check to Time-of-Use (TOCTOU) race condition in its unauthenticated onboarding API endpoint. This flaw allows remote, unauthenticated attackers to register an administrative account by sending concurrent HTTP requests during the initial system configuration phase, bypassing the check meant to restrict onboarding to a single initial administrator.
A logic error in Hatchet's OAuth state validation mechanism allows unauthenticated remote attackers to bypass state parameter verification. By submitting an empty state parameter, attackers can exploit an equality collision with cleared session keys, facilitating Login Cross-Site Request Forgery (Login CSRF) or Session Fixation.
A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.