CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-108262

CVE-2026-108262: Arbitrary Host File Overwrite via Tar Symlink Traversal in DDEV

Amit Schendel
Amit Schendel
Senior Security Researcher

Nov 10, 2026·10 min read·1 visit

Executive Summary (TL;DR)

An incomplete fix for CVE-2026-32885 in DDEV versions 1.25.2 and 1.25.3 allows malicious tar archives containing absolute symlink targets to bypass extraction directory containment, resulting in arbitrary file overwrite on the host filesystem.

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.

Vulnerability Overview

DDEV is an open-source tool designed to provision and manage local PHP and Node.js development environments using Docker containers. As part of its core workflow, DDEV frequently ingests and extracts compressed tar archives (.tar, .tar.gz, .tar.bz2, .tar.xz) on the host machine. These operations occur during database imports (ddev import-db), static asset imports (ddev import-files), and third-party add-on installations (ddev get). The archive extraction logic is centralized within the Untar function located in pkg/archive/archive.go.

Because archive extraction runs directly on the host operating system rather than inside an isolated container, pkg/archive/archive.go exposes a direct host filesystem attack surface when processing untrusted archives. To maintain security, the extraction routine must strictly confine all extracted files, directories, and symbolic links within the designated target directory (dest). Failure to enforce boundary checks during archive extraction exposes the host system to path traversal attacks.

CVE-2026-108262 (also tracked as GHSA-9hq4-hm3j-jmph) is classified under both CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) and CWE-59 (Improper Link Resolution Before File Access). The vulnerability affects DDEV versions 1.25.2 and 1.25.3, and was introduced directly by commit 662ea61e3c453e16a25b4630e78417e3087e36c6 as an incomplete remediation for an earlier path traversal vulnerability, CVE-2026-32885.

By crafting a tar archive containing a symbolic link with an absolute target path followed by a regular file entry that traverses that symbolic link, an attacker can write or overwrite arbitrary files anywhere on the host filesystem where the DDEV user has write permissions. Notably, the Unzip functionality within the same package is unaffected by this flaw.

Root Cause Analysis

The root cause of CVE-2026-108262 is a state discrepancy between the path value validated during the security check and the path value passed to the operating system's symlink creation syscall. When remediating CVE-2026-32885 in DDEV v1.25.2, the developers implemented validation logic inside Untar() to prevent symbolic links from pointing outside the extraction root dest. However, the implementation attempted to preserve compatibility with container-originating archives that might contain absolute symlink targets (such as /var/www/html).

When Untar() encounters a tar header of type tar.TypeSymlink, it inspects the link target stored in file.Linkname. In vulnerable versions, the code checks if file.Linkname is an absolute path using filepath.IsAbs(file.Linkname). If filepath.IsAbs returns true, the function constructs a synthetic validation path by joining the destination directory and the absolute link target: resolvedTarget = filepath.Join(dest, file.Linkname).

In Go's path/filepath library, filepath.Join concatenates path elements and cleans the result, stripping leading slashes from subsequent arguments. Consequently, if dest is /tmp/extract and file.Linkname is /home/victim/.ssh, filepath.Join evaluates resolvedTarget to /tmp/extract/home/victim/.ssh. When DDEV subsequently verifies whether resolvedTarget begins with the prefix /tmp/extract/, the validation check unconditionally succeeds for any absolute path that does not contain excessive .. segments.

Following this validation step, Untar() creates the symbolic link on disk by invoking os.Symlink(file.Linkname, fullPath). Rather than passing the rebased resolvedTarget variable that was just validated, the code passes the raw, unmodified file.Linkname. This plants a live symbolic link inside the extraction directory pointing directly to the absolute host path /home/victim/.ssh.

Once the absolute symbolic link is planted on the filesystem, the second stage of the vulnerability—symlink write-through traversal—is possible. When the tar reader processes a subsequent regular file entry (tar.TypeReg) located at link/authorized_keys, the lexical path validation checks the string <dest>/link/authorized_keys. Because this string lexically resides under <dest>/, the lexical check passes. When os.OpenFile or os.Create is subsequently invoked on <dest>/link/authorized_keys, the operating system kernel dereferences the link directory component, writing the attacker's payload directly to the host path /home/victim/.ssh/authorized_keys.

Code Analysis

The vulnerable code resides in pkg/archive/archive.go within the Untar function. In DDEV 1.25.2 and 1.25.3, the tar.TypeSymlink switch case handled path resolution and symlink creation as follows:

// Vulnerable logic in pkg/archive/archive.go (v1.25.2 - v1.25.3)
var resolvedTarget string
if filepath.IsAbs(file.Linkname) {
    // BUG 1: filepath.Join strips the leading slash from file.Linkname,
    // rebasing the path under 'dest' strictly in memory.
    resolvedTarget = filepath.Join(dest, file.Linkname)
} else {
    resolvedTarget = filepath.Join(fullPathDir, file.Linkname)
}
 
// The prefix check validates 'resolvedTarget' (<dest>/<file.Linkname>),
// which always passes for simple absolute paths like "/etc" or "/Users/admin".
if !strings.HasPrefix(filepath.Clean(resolvedTarget)+string(os.PathSeparator), filepath.Clean(dest)+string(os.PathSeparator)) {
    return fmt.Errorf("symlink target %q in archive entry %q escapes destination directory", file.Linkname, file.Name)
}
 
// Remove any existing file/symlink at this path
_ = os.Remove(fullPath)
 
// BUG 2: os.Symlink uses the raw, unvalidated 'file.Linkname' instead of 'resolvedTarget'.
err = os.Symlink(file.Linkname, fullPath)
if err != nil {
    return fmt.Errorf("failed to create symlink %v -> %v, err: %v", fullPath, file.Linkname, err)
}

In commit 7034963403c5374fecceb02070d27b335329cede (merged via PR #8625 in commit 5da91aeb9ebab0b0e66171c450b72099308d332c), the DDEV maintainers eliminated the rebasing mechanism entirely. They determined that archives processed on the host have no legitimate reason to establish absolute symbolic links.

// Patched logic in pkg/archive/archive.go (v1.25.4)
func isArchiveAbsolutePath(p string) bool {
    // Explicitly check POSIX '/', Windows '\', and host-native filepath.IsAbs
    return strings.HasPrefix(p, "/") || strings.HasPrefix(p, `\`) || filepath.IsAbs(p)
}
 
// Inside Untar() -> case tar.TypeSymlink:
if isArchiveAbsolutePath(file.Linkname) {
    return fmt.Errorf("symlink target %q in archive entry %q is absolute, which is not allowed", file.Linkname, file.Name)
}
 
// Only relative targets are permitted and are resolved against fullPathDir
resolvedTarget := filepath.Join(fullPathDir, file.Linkname)
if !strings.HasPrefix(filepath.Clean(resolvedTarget)+string(os.PathSeparator), filepath.Clean(dest)+string(os.PathSeparator)) {
    return fmt.Errorf("symlink target %q in archive entry %q escapes destination directory", file.Linkname, file.Name)
}
 
_ = os.Remove(fullPath)
 
err = os.Symlink(file.Linkname, fullPath)
if err != nil {
    return fmt.Errorf("failed to create symlink %v -> %v, err: %v", fullPath, file.Linkname, err)
}

Fix Completeness Assessment

A critical aspect of the patch in commit 7034963403c5374fecceb02070d27b335329cede is the introduction of the helper function isArchiveAbsolutePath(p string). Relying solely on Go's standard filepath.IsAbs(p) is insufficient for cross-platform archive extraction because filepath.IsAbs is host-OS dependent. On Windows hosts, filepath.IsAbs("/Users/victim") or filepath.IsAbs("\\Users\\victim") returns false because a drive letter or UNC share is absent, yet passing such a path to os.Symlink on Windows still creates a symlink rooted at the current drive (e.g., C:\Users\victim). By explicitly rejecting leading forward slashes (/), leading backslashes (\), and any path matching filepath.IsAbs(p), the patch prevents cross-platform bypasses.

However, it is worth examining whether relative symlinks can still be chained or abused. Because isArchiveAbsolutePath blocks all absolute targets, file.Linkname is guaranteed to be relative. When filepath.Join(fullPathDir, file.Linkname) is evaluated, filepath.Clean collapses any ../ sequences relative to fullPathDir, and the strings.HasPrefix check ensures the resolved target remains strictly inside dest. Provided that fullPathDir itself has not already traversed outside dest via an earlier symlink, the containment holds.

Exploitation

Exploiting CVE-2026-108262 requires an attacker to supply a crafted tar archive (.tar, .tar.gz, .tar.bz2, or .tar.xz) to a user running DDEV version 1.25.2 or 1.25.3. No elevated privileges on the target machine are required, though user interaction is necessary to trigger the archive extraction operation. Common delivery vectors include publishing a malicious third-party DDEV add-on retrieved via ddev get <attacker-repo>, or supplying a trojanized project database/asset dump imported via ddev import-db or ddev import-files.

Constructing the exploit archive requires two sequential tar entries:

  1. Symlink Planting Entry: A tar header with Typeflag: tar.TypeSymlink, an arbitrary relative name within the archive (e.g., Name: "link"), and Linkname set to the target absolute directory on the host filesystem (e.g., /home/username/.ssh or /Users/username).
  2. Payload Delivery Entry: A tar header with Typeflag: tar.TypeReg whose Name traverses the symlink created in the first entry (e.g., Name: "link/authorized_keys" or Name: "link/.zshrc"), followed by the attacker's file content.

The official patch commit includes a complete proof-of-concept regression test in pkg/archive/archive_test.go that demonstrates the exact exploitation mechanics:

// Proof-of-Concept from pkg/archive/archive_test.go
t.Run("absolute_symlink_write_through_escape", func(t *testing.T) {
    victimDir := t.TempDir()
    victimFile := filepath.Join(victimDir, "pwned.txt")
    require.NoError(t, os.WriteFile(victimFile, []byte("original"), 0644))
 
    f, err := os.CreateTemp("", "absolute_symlink_write_through_escape_*.tar")
    require.NoError(t, err)
    t.Cleanup(func() { _ = os.Remove(f.Name()) })
 
    tw := tar.NewWriter(f)
    // Step 1: Plant symlink 'link' pointing to absolute host path 'victimDir'
    require.NoError(t, tw.WriteHeader(&tar.Header{
        Name:     "link",
        Typeflag: tar.TypeSymlink,
        Linkname: victimDir,
        Mode:     0777,
    }))
 
    // Step 2: Write regular file through 'link/pwned.txt'
    content := []byte("OWNED-BY-ARCHIVE")
    require.NoError(t, tw.WriteHeader(&tar.Header{
        Name:     "link/pwned.txt",
        Typeflag: tar.TypeReg,
        Size:     int64(len(content)),
        Mode:     0644,
    }))
    _, err = tw.Write(content)
    require.NoError(t, err)
    require.NoError(t, tw.Close())
    require.NoError(t, f.Close())
 
    writeThroughDest := testcommon.CreateTmpDir("absolute_symlink_write_through_escape")
    t.Cleanup(func() { _ = os.RemoveAll(writeThroughDest) })
 
    // On DDEV 1.25.2 and 1.25.3, this overwrites victimFile on the host
    err = archive.Untar(f.Name(), writeThroughDest, "")
})

> [!NOTE] > In practical attack scenarios where the attacker does not know the exact username of the target developer, the archive can include multiple symlink/file pairs targeting common paths (e.g., /root/.bashrc, /home/ubuntu/.bashrc, /var/spool/cron/crontabs/...), or target project-level paths if the workspace directory structure is predictable.

Impact Assessment

The direct technical impact of CVE-2026-108262 is arbitrary file creation and overwrite on the host filesystem with the privileges of the local user running the ddev CLI. Because DDEV runs as a standard user application on the developer's workstation rather than inside a restricted sandbox during initial archive extraction, the scope of writable locations encompasses the entire user home directory, project repositories, SSH configurations, and local credential stores.

An attacker who successfully exploits this vulnerability can escalate from arbitrary file write to remote code execution (RCE) and persistence on the developer's host machine. Specifically, overwriting shell initialization files (~/.bashrc, ~/.zshrc, ~/.profile), SSH configuration files (~/.ssh/authorized_keys, ~/.ssh/config), or local Git hooks (.git/hooks/pre-commit, .git/hooks/post-checkout) guarantees code execution the next time the user opens a terminal, connects via SSH, or runs a Git command.

The official CVSS v3.1 score is 6.5 (Medium) with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N. The score reflects high integrity impact (I:H) due to unrestricted filesystem modification, low attack complexity (AC:L), and required user interaction (UI:R) to initiate the import or add-on installation command. Confidentiality and availability are scored as None (C:N/A:N) in the base vector because the vulnerability primitive itself is strictly a file write mechanism, though secondary post-exploitation effects routinely compromise confidentiality.

From a threat intelligence perspective, the EPSS score for CVE-2026-108262 is 0.00306 (23.60th percentile), indicating a low probability of widespread automated exploitation in the wild. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no public weaponized exploits currently exist on ExploitDB or PacketStorm.

Remediation

To remediate CVE-2026-108262, users and administrators must upgrade DDEV to version 1.25.4 or later. Version 1.25.4 incorporates commit 7034963403c5374fecceb02070d27b335329cede, which strictly rejects any tar symlink entry containing an absolute target path.

Developers can verify their currently installed DDEV version by running the following command in their terminal:

ddev --version

If the output indicates version v1.25.2 or v1.25.3, upgrade immediately using the appropriate package manager for the host operating system (such as Homebrew on macOS/Linux, apt/rpm repositories on Linux, or Winget/Chocolatey on Windows).

For environments where an immediate upgrade to v1.25.4 is not possible, users must avoid running ddev get, ddev import-db, or ddev import-files against untrusted or externally sourced tar archives. Before importing any archive on a vulnerable version, operators can manually inspect the archive's table of contents to verify that no symbolic links point to absolute paths:

# Inspect tar archive for symlink entries (marked with leading 'l' in permissions)
tar -tvf archive.tar.gz | grep -E '^l'

If any entry in the output displays a link target beginning with /, \, or a Windows drive letter (e.g., link -> /etc/passwd or link -> C:\Users), the archive must be rejected and deleted immediately.

Official Patches

DDEVGitHub Security Advisory GHSA-9hq4-hm3j-jmph
DDEVOfficial Patch Commit in ddev/ddev
DDEVPull Request #8625 fixing absolute symlink escape
DDEVDDEV v1.25.4 Official Release

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
EPSS Probability
0.31%
Top 76% most exploited

Affected Systems

DDEV (ddev/ddev)

Affected Versions Detail

Product
Affected Versions
Fixed Version
ddev
DDEV
>= 1.25.2, < 1.25.41.25.4
AttributeDetail
Vulnerability TypePath Traversal / Arbitrary File Overwrite (CWE-22, CWE-59)
Affected Componentpkg/archive/archive.go (Untar function)
Affected VersionsDDEV >= 1.25.2, < 1.25.4 (v1.25.2, v1.25.3)
CVSS v3.1 Score6.5 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N)
EPSS Score0.31% (23.60th Percentile)
Exploit StatusProof-of-Concept Available (Unit Test in Patch)
CISA KEV StatusNot Listed
Patched VersionDDEV 1.25.4

MITRE ATT&CK Mapping

T1083File and Directory Discovery
Discovery
T1005Data from Local System
Collection
CWE-22
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Known Exploits & Detection

GitHubOfficial regression test (absolute_symlink_write_through_escape) demonstrating arbitrary file overwrite via tar symlink traversal in pkg/archive/archive_test.go.

Vulnerability Timeline

CVE-2026-108262 Reserved
2026-10-09
CVE-2026-108262 and GHSA-9hq4-hm3j-jmph Published
2026-11-06
DDEV v1.25.4 Released with Fix
2026-11-06
OSV Advisory Record Updated
2026-11-08

References & Sources

  • [1]GitHub Security Advisory: GHSA-9hq4-hm3j-jmph
  • [2]DDEV Fix Commit 7034963403c5374fecceb02070d27b335329cede
  • [3]DDEV Pull Request #8625
  • [4]DDEV Release v1.25.4
  • [5]NVD - CVE-2026-108262
  • [6]CVE.org Record: CVE-2026-108262
  • [7]CVEListV5 JSON Record for CVE-2026-108262
Related Vulnerabilities
CVE-2026-32885

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•CVE-2026-119298
4.2

CVE-2026-119298: Overwrite Confirmation Bypass in schollz/croc via Unvalidated Text Artifact Prefix

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.

Amit Schendel
Amit Schendel
3 views•9 min read
•about 2 hours ago•CVE-2026-88968
7.7

CVE-2026-88968: TOCTOU Race Condition in Claude Code Edit Tool Allows Arbitrary File Overwrite

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.

Alon Barad
Alon Barad
4 views•9 min read
•about 3 hours ago•CVE-2026-88982
5.4

CVE-2026-88982: Cross-Tenant Execution Cache Poisoning and Output Substitution in Kubeflow Pipelines

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.

Amit Schendel
Amit Schendel
8 views•12 min read
•about 4 hours ago•CVE-2026-88983
5.0

CVE-2026-88983: Authenticated Server-Side Request Forgery in Kubeflow Pipelines API Server

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.

Amit Schendel
Amit Schendel
5 views•11 min read
•about 5 hours ago•CVE-2026-88985
6.5

CVE-2026-88985: SQL Injection via Pagination Token Deserialization and Metric Sorting in Kubeflow Pipelines API Server

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.

Alon Barad
Alon Barad
4 views•10 min read
•about 6 hours ago•CVE-2026-88986
6.1

CVE-2026-88986: Reflected Cross-Site Scripting in Kubeflow Pipelines Frontend Error Responses

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.

Alon Barad
Alon Barad
6 views•10 min read