Nov 10, 2026·9 min read·3 visits
Versions of the Go-based peer-to-peer file transfer utility croc prior to 11.5.2 trust sender-supplied filenames prefixed with 'croc-stdin-' during standard file transfers. This allows an untrusted sender to bypass both interactive overwrite prompts and the --rename flag, silently overwriting or truncating existing files matching that prefix in the receiver's destination directory.
CVE-2026-119298 (GHSA-x89h-7h96-v88f) is a file overwrite confirmation bypass vulnerability in schollz/croc prior to version 11.5.2. The receive-side file validation logic in src/croc/croc.go improperly exempted any incoming file whose basename began with the prefix 'croc-stdin-' from interactive overwrite prompts and automatic collision renaming, failing to verify if the transfer session was actually an internal text transfer. An unauthenticated remote peer participating in a transfer session can exploit this flaw to silently overwrite, truncate, or replace matching pre-existing files in the receiver's working directory.
schollz/croc is a peer-to-peer file and text transfer command-line utility written in Go. It facilitates direct data transfers between systems using a relay server and shared code phrases. Because the tool accepts file metadata and payloads from remote peers over untrusted networks, the receive-side filesystem handling routines represent a primary security boundary.
In versions of croc prior to 11.5.2, a flaw exists in how the receiver validates incoming filenames before writing them to the local filesystem. Tracked as CVE-2026-119298 (and GitHub Security Advisory GHSA-x89h-7h96-v88f), the vulnerability is classified under CWE-73 (External Control of File Name or Path). Specifically, the function updateIfRecipientHasFileInfo() in src/croc/croc.go exempts any file whose basename starts with croc-stdin- from collision checks, regardless of whether the session is a text transfer or a standard file transfer.
When a receiver accepts a transfer session from an untrusted sender, the sender controls the fileInfo.Name metadata field for each transmitted object. By prefixing a regular file, empty file, or symlink with croc-stdin-, the sender bypasses both the interactive overwrite confirmation prompt (askReceiveOverwrite) and the automatic collision renaming mechanism (--rename).
Consequently, any pre-existing file in the receiver's current working directory matching the croc-stdin-* naming pattern is silently modified, truncated, or unlinked and replaced. While path sanitization routines prevent directory traversal outside the target receive directory, the integrity and availability of local files within the croc-stdin- namespace are compromised without user consent.
To understand the root cause of CVE-2026-119298, it is necessary to examine how croc differentiates between standard file transfers and piped standard input (stdin) or text transfers. When a user initiates a text transfer (for example, via croc send --text "message"), the sender sets the boolean flag SendingText: true inside the session metadata (SenderInfo). Upon receiving a text transfer, the recipient client programmatically generates a temporary local filename prefixed with croc-stdin- to buffer the incoming stream.
Because these croc-stdin- artifacts are intended to be temporary buffers managed internally by the receiver, the application suppresses interactive overwrite prompts and automatic file renaming for them. However, prior to version 11.5.2, the state check inside updateIfRecipientHasFileInfo() determined whether a file was an internal text artifact solely by inspecting the filename prefix rather than checking the session state.
In the vulnerable implementation, the boolean variable isStdinName was assigned using strings.HasPrefix(path.Base(fileInfo.Name), "croc-stdin-"). During a standard file transfer (c.Options.SendingText == false), fileInfo.Name is populated directly from the untrusted sender's transfer offer. The code failed to verify whether c.Options.SendingText was set to true before trusting the croc-stdin- prefix.
When a local file with the same name already existed (destinationExists == true) and its hash differed from the incoming file (!hashesEqual), the conditions !isStdinName evaluated to false. This caused the execution flow to skip both utils.UnusedFilename() (when --rename was active) and askReceiveOverwrite() (under default interactive settings). The client then proceeded directly to recipientInitializeFile() or createEmptyFileAndFinish(), destroying the existing local file.
The vulnerability resides in src/croc/croc.go within the (*Client).updateIfRecipientHasFileInfo() method. When the recipient inspects the list of files offered by the sender, it checks if each file already exists on disk and compares cryptographic hashes to determine if a transfer resume or overwrite is required.
In versions prior to 11.5.2, the collision handling logic was implemented as follows:
// Vulnerable code in src/croc/croc.go (prior to v11.5.2)
if !hashesEqual {
log.Debugf("hashed %s to %x using %s", fileInfo.Name, fileHash, c.Options.HashAlgorithm)
log.Debugf("hashes are not equal %x != %x", fileHash, fileInfo.Hash)
// FLAW: Relies exclusively on the sender-controlled basename
isStdinName := strings.HasPrefix(path.Base(fileInfo.Name), "croc-stdin-")
// Bypassed when isStdinName is true
if destinationExists && !isStdinName && c.Options.Rename {
newName := utils.UnusedFilename(fileInfo.FolderRemote, fileInfo.Name)
output, colorEnabled := termui.Output(os.Stderr)
fmt.Fprintf(output, "Receiving %s as %s\n", quotedFilename(fileInfo.Name, colorEnabled), quotedFilename(newName, colorEnabled))
c.FilesToTransfer[i].Name = newName
fileInfo.Name = newName
destinationExists = false
}
// Bypassed when isStdinName is true
if destinationExists && !c.Options.Overwrite && !c.Options.Rename && !isStdinName {
resumable := fileInfo.Size > 0 && fileInfo.Symlink == "" && recipientFileInfo.Mode().IsRegular()
overwrite, promptErr := askReceiveOverwrite(c.clientContext(), fileInfo, resumable)
if promptErr != nil {
return promptErr
}
// ...
}
}In commit 25fe49b6f14db99f517e902cfef76299ad0a95a3 (merged via Pull Request #1327), the maintainer remediated the issue by binding the prefix exemption directly to the c.Options.SendingText session state:
File: src/croc/croc.go
@@ -3410,16 +3410,18 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
if !hashesEqual {
log.Debugf("hashed %s to %x using %s", fileInfo.Name, fileHash, c.Options.HashAlgorithm)
log.Debugf("hashes are not equal %x != %x", fileHash, fileInfo.Hash)
- isStdinName := strings.HasPrefix(path.Base(fileInfo.Name), "croc-stdin-")
- if destinationExists && !isStdinName && c.Options.Rename {
+ // The croc-stdin- exemption is reserved for validated text transfers,
+ // whose destination names are generated by the receiver.
+ isTextArtifact := c.Options.SendingText && strings.HasPrefix(path.Base(fileInfo.Name), "croc-stdin-")
+ if destinationExists && !isTextArtifact && c.Options.Rename {
newName := utils.UnusedFilename(fileInfo.FolderRemote, fileInfo.Name)
output, colorEnabled := termui.Output(os.Stderr)
fmt.Fprintf(output, "Receiving %s as %s\n", quotedFilename(fileInfo.Name, colorEnabled), quotedFilename(newName, colorEnabled))
c.FilesToTransfer[i].Name = newName
fileInfo.Name = newName
destinationExists = false
}
- if destinationExists && !c.Options.Overwrite && !c.Options.Rename && !isStdinName {
+ if destinationExists && !c.Options.Overwrite && !c.Options.Rename && !isTextArtifact {
resumable := fileInfo.Size > 0 && fileInfo.Symlink == "" && recipientFileInfo.Mode().IsRegular()
overwrite, promptErr := askReceiveOverwrite(c.clientContext(), fileInfo, resumable)
if promptErr != nil {The patch replaces isStdinName with isTextArtifact, which requires both c.Options.SendingText and the croc-stdin- prefix to be true. Because validated text transfers have their destination names generated on the receiver side rather than taken from sender file paths, an attacker can no longer force isTextArtifact to evaluate to true during a file transfer session. The fix is complete for this code path and is accompanied by unit tests in src/croc/overwrite_test.go (TestReceiveStdinNamedFileCollision and TestReceiveTextArtifactDoesNotPromptOrRename).
Exploiting CVE-2026-119298 requires a peer-to-peer transfer scenario where a victim receiver accepts an incoming session from an untrusted sender. No authentication to the target host is required beyond coordinating the transfer code phrase, which satisfies the User Interaction (UI:R) requirement in the CVSS vector. However, Attack Complexity is High (AC:H) because the attacker must target a pre-existing file in the receiver's working directory that starts with croc-stdin-.
To execute the attack, the sender prepares a payload matching a target filename, such as croc-stdin-notes.txt. The payload can take one of three forms, each triggering a specific filesystem operation on the receiver without prompting:
recipientInitializeFile opens the existing local file and truncates or overwrites its data blocks with the sender's payload.createEmptyFileAndFinish, which deletes the existing file and creates a new 0-byte file in its place.croc-stdin-notes.txt pointing to a relative target within the receive directory (e.g., safe-target.txt), createEmptyFileAndFinish removes the original regular file and replaces it with the symlink.> [!NOTE]
> Even if the receiver explicitly passes the --rename flag on the command line to prevent file collisions, the vulnerable !isStdinName check overrides the user's configuration and forces an in-place overwrite.
The upstream regression test TestReceiveStdinNamedFileCollision in src/croc/overwrite_test.go demonstrates these exact collision primitives across five payload variations: same-size, shorter, longer, empty, and symlink. Prior to the patch, declining a prompt or closing stdin had no effect on croc-stdin-* files because the prompt function askReceiveOverwrite was never invoked.
CVE-2026-119298 is assigned a CVSS v3.1 base score of 4.2 (Medium) with the vector CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:L. The vulnerability has no impact on Confidentiality (C:N), as the overwrite primitive is strictly one-way from sender to receiver and does not exfiltrate local filesystem data back to the sender.
The Integrity (I:L) and Availability (A:L) impacts are both rated Low because the scope of modifiable files is strictly constrained to the croc-stdin-* namespace within the destination directory. An attacker can corrupt stored data (MITRE ATT&CK T1565.001) or truncate local text artifacts and logs matching the prefix (T1070), resulting in data loss for those specific files.
Crucially, this vulnerability cannot be chained into arbitrary directory traversal or arbitrary dotfile replacement (such as overwriting .bashrc or .ssh/authorized_keys). croc enforces separate path sanitization checks that strip or reject parent directory traversal sequences (..), and symlink targets are independently restricted to the receive directory via validateReceiveSymlinkTarget.
As of November 2026, the Exploit Prediction Scoring System (EPSS) assigns CVE-2026-119298 a score of 0.00302 (23.07th percentile), indicating a low probability of active exploitation in the wild. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no standalone weaponized exploits have been published outside of the upstream unit tests.
The primary remediation for CVE-2026-119298 is to upgrade schollz/croc to version 11.5.2 or later. Version 11.5.2 ensures that the croc-stdin- filename exemption is strictly gated behind c.Options.SendingText, restoring interactive overwrite confirmation prompts and --rename functionality for all standard file transfers.
Users and administrators can upgrade croc using the official installation script or directly via the Go toolchain:
# Upgrade via the Go toolchain
go install github.com/schollz/croc/v11@v11.5.2In environments where an immediate binary upgrade is not feasible, users can mitigate the risk of file collisions by isolating incoming transfers into dedicated, empty staging directories. Using the --out CLI flag directs all incoming writes away from active working directories:
# Isolate incoming files in a freshly created temporary directory
mkdir -p /tmp/croc-staging && croc --out /tmp/croc-staging <code-phrase>Additionally, operators should avoid executing croc in directories that store persistent artifacts prefixed with croc-stdin- and should only accept transfer code phrases from verified peers. Host-based file integrity monitoring (FIM) or auditd rules can also be configured to alert on unexpected O_TRUNC or unlink system calls targeting croc-stdin-* files during active transfer sessions.
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
croc schollz | < 11.5.2 | 11.5.2 |
| Attribute | Detail |
|---|---|
| CVE / Advisory ID | CVE-2026-119298 / GHSA-x89h-7h96-v88f |
| Weakness Class | CWE-73: External Control of File Name or Path |
| Attack Vector | Network (Peer-to-Peer Transfer Session) |
| CVSS v3.1 Score | 4.2 Medium (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:L) |
| EPSS Score | 0.302% (23.07th Percentile) |
| Impact | Silent overwrite, truncation, or symlink replacement of 'croc-stdin-*' files |
| Exploit Status | Unit Test PoC in Upstream Repository |
| CISA KEV Status | Not Listed |
The software allows user input to control or influence paths or file names that are used in filesystem operations without properly validating or scoping the context of the filename.
CVE-2026-108262 (GHSA-9hq4-hm3j-jmph) is a path traversal and arbitrary file overwrite vulnerability in the tar archive extraction routine of DDEV versions 1.25.2 and 1.25.3. Resulting from an incomplete fix for CVE-2026-32885, the Untar function in pkg/archive/archive.go validates absolute symbolic link targets by rebasing them against the extraction destination directory, but subsequently creates the symbolic link on disk using the raw, unvalidated absolute path. When subsequent entries in the archive write through the planted symbolic link, the host operating system resolves the link directly to the host filesystem, allowing an attacker to overwrite arbitrary files with the privileges of the user executing DDEV.
Claude Code prior to version 2.1.251 contains a Time-of-Check to Time-of-Use (TOCTOU) race condition in its internal file manipulation tools (Edit, Write, Read). Because path validation was performed using string-based pathname and symbolic link resolution without binding to the underlying directory inodes or open file descriptors, a process running concurrently inside the macOS Bash sandbox can atomically replace an in-workspace directory with a symbolic link after validation passes. This causes the unsandboxed main process to follow the symlink and overwrite user-writable files outside the permitted workspace boundary.
CVE-2026-88982 (GHSA-2xwh-426q-42c5) is an authorization bypass and data integrity vulnerability affecting Kubeflow Pipelines versions prior to 2.18.0. The flaw resides in the legacy V1 and raw-Argo execution caching mutating admission webhook and the associated cache-server storage backend. In multi-tenant deployments where namespaces share a centralized cache service, execution cache keys were derived solely from container specification fields without incorporating the workload's Kubernetes namespace. Additionally, the cache watcher trusted mutable pod annotations when persisting execution outputs. Consequently, an authenticated user with workload submission privileges in one tenant namespace can poison cached step outputs consumed by other tenants or read cached outputs generated by workloads in separate namespaces.
CVE-2026-88983 is an authenticated Server-Side Request Forgery (SSRF) vulnerability affecting the Kubeflow Pipelines API server in versions prior to 2.17.0. When an authenticated user creates a pipeline or pipeline version using a remote specification URI, the backend server retrieves the document via Go's default HTTP client without validating the target scheme, hostname, port, or IP address. Because the API server reflects remote connection errors and HTTP status codes in its response, an attacker with pipeline creation permissions can exploit the server as a status-code and port-scanning oracle against Kubernetes cluster-internal services, private networks, and cloud metadata endpoints.
CVE-2026-88985 is a SQL injection vulnerability (CWE-89) in the Kubeflow Pipelines (KFP) API server prior to version 2.17.0. The flaw stems from the untrusted deserialization of base64-encoded JSON pagination tokens (page_token) and the direct concatenation of metric names during run list sorting. Authenticated users with permissions to invoke REST or gRPC list endpoints can inject arbitrary SQL syntax into structural query identifiers and JOIN predicates, enabling unauthorized cross-tenant read access to all database records accessible by the API server.
A reflected Cross-Site Scripting (XSS) vulnerability exists in the Kubeflow Pipelines frontend server prior to version 2.15.0. Specifically, the Express route handlers for '/pipeline/k8s/pod' and '/pipeline/k8s/pod/events' reflect unvalidated 'podname' and 'podnamespace' query parameters, along with backend Kubernetes API error messages, directly into HTML-capable HTTP error responses. An unauthenticated attacker can exploit this flaw by convincing an authenticated user to visit a crafted URL, resulting in arbitrary JavaScript execution within the context of the Kubeflow Pipelines frontend origin.