CVE-2026-53476
Description
A path traversal vulnerability in assisted-migration-agent allows unauthenticated LAN attackers to write arbitrary files and achieve code execution.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A path traversal vulnerability in assisted-migration-agent allows unauthenticated LAN attackers to write arbitrary files and achieve code execution.
Vulnerability
A path traversal vulnerability exists in the assisted-migration-agent within the internal/services/vddk.go file, specifically in the extractTarGz() function used by the PUT /inspector/vddk endpoint. This flaw affects versions of the agent that use this extraction logic. The vulnerability arises because the path traversal protection mechanism relies on lexical checks that do not resolve symlinks already present on the filesystem, allowing for chained symlink attacks [2].
Exploitation
An unauthenticated attacker on the same local area network (LAN) can exploit this vulnerability. The attacker must craft a specially designed gzipped tarball. This tarball would contain a sequence of entries, such as a symlink a/x pointing to .., followed by a file a/x/y. The extractor's lexical checks would pass the initial symlink, but the subsequent file write would follow the symlink, writing to a location outside the intended extraction directory [2, 3].
Impact
Successful exploitation allows an attacker to write arbitrary files to the system with the privileges of UID 1001. Writable targets include configuration directories like /var/lib/agent/config and cache directories like /app/.cache. This capability can lead to persistent code execution on the appliance and potentially allow for the exfiltration of sensitive credentials, such as vCenter admin credentials, ultimately enabling unauthorized code execution [2, 3].
Mitigation
A fix has been implemented that resolves this vulnerability. The suggested mitigation involves resolving the parent directory of each entry using filepath.EvalSymlinks before creating it and re-verifying that the resolved path remains within the intended destination directory. This approach preserves legitimate VDDK symlinks while preventing malicious ones. The specific patch is available in reference [3]. The fixed version and release date are not explicitly disclosed in the available references, but the vulnerability is listed on Red Hat's security advisory [1].
AI Insight generated on Jun 10, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)
Patches
1bcae0438ad83ECOPROJECT-4719 | fix: Prevent symlink-based path traversal in VDDK tarball extraction
2 files changed · +101 −0
internal/services/vddk.go+20 −0 modified@@ -185,6 +185,26 @@ func extractTarGz(r io.Reader, destDir string) error { return fmt.Errorf("illegal file path: %s", targetPath) } + // ECOPROJECT-4719 | Before creating files/directories, resolve symlinks in the parent path + // to prevent chained symlink attacks (e.g., a/x -> .., then a/x/evil) + if header.Typeflag == tar.TypeDir || header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeSymlink { + parentDir := filepath.Dir(targetPath) + if parentDir != destDir { + resolvedParent, err := filepath.EvalSymlinks(parentDir) + if err == nil { + // Parent exists - verify resolved path is still strictly inside destDir. + // If it resolves to destDir itself, that means a symlink has traversed + // back to the root, which indicates an escape attempt. + if filepath.Clean(resolvedParent) == filepath.Clean(destDir) { + return fmt.Errorf("symlink escape detected: %s resolves to %s (root)", parentDir, resolvedParent) + } + if !pathInsideDest(destDir, resolvedParent) { + return fmt.Errorf("symlink escape detected: %s resolves to %s", parentDir, resolvedParent) + } + } + } + } + switch header.Typeflag { case tar.TypeDir: if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)&0o755); err != nil {
internal/services/vddk_test.go+81 −0 modified@@ -260,6 +260,87 @@ var _ = Describe("VddkService", func() { }) }) + Describe("Security: Path Traversal Prevention", func() { + It("blocks chained symlink attack", func() { + tarGz := test.BuildTarGz( + test.TarEntry{ + Path: "a/x", + LinkTarget: "..", + }, + test.TarEntry{ + Path: "a/x/evil.sh", + Content: "malicious payload", + }, + ) + filename := "VMware-vix-disklib-8.0.3-23950268.x86_64.tar.gz" + _, err := srv.Upload(context.Background(), filename, bytes.NewReader(tarGz)) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("symlink escape detected")) + }) + + It("blocks absolute symlink escape", func() { + tarGz := test.BuildTarGz( + test.TarEntry{ + Path: "malicious", + LinkTarget: "/etc/passwd", + }, + ) + filename := "VMware-vix-disklib-8.0.3-23950268.x86_64.tar.gz" + _, err := srv.Upload(context.Background(), filename, bytes.NewReader(tarGz)) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("illegal symlink target")) + }) + + It("blocks relative symlink pointing outside destDir", func() { + tarGz := test.BuildTarGz( + test.TarEntry{ + Path: "a/b/c", + LinkTarget: "../../../etc/passwd", + }, + ) + filename := "VMware-vix-disklib-8.0.3-23950268.x86_64.tar.gz" + _, err := srv.Upload(context.Background(), filename, bytes.NewReader(tarGz)) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("illegal symlink target")) + }) + + It("allows legitimate VDDK internal symlinks", func() { + // VDDK tarballs contain .so version symlinks like libcares.so -> libcares.so.2 + tarGz := test.BuildTarGz( + test.TarEntry{ + Path: "vmware-vix-disklib-distrib/lib64/libvixDiskLib.so.8.0.3", + Content: "library-content", + }, + test.TarEntry{ + Path: "vmware-vix-disklib-distrib/lib64/libvixDiskLib.so", + LinkTarget: "libvixDiskLib.so.8.0.3", + }, + ) + filename := "VMware-vix-disklib-8.0.3-23950268.x86_64.tar.gz" + _, err := srv.Upload(context.Background(), filename, bytes.NewReader(tarGz)) + Expect(err).NotTo(HaveOccurred()) + + // Verify symlink was created correctly + link := filepath.Join(dataDir, "vddk", "vmware-vix-disklib-distrib", "lib64", "libvixDiskLib.so") + target, err := os.Readlink(link) + Expect(err).NotTo(HaveOccurred()) + Expect(target).To(Equal("libvixDiskLib.so.8.0.3")) + }) + + It("blocks directory traversal with clean paths", func() { + tarGz := test.BuildTarGz( + test.TarEntry{ + Path: "../../etc/shadow", + Content: "malicious", + }, + ) + filename := "VMware-vix-disklib-8.0.3-23950268.x86_64.tar.gz" + _, err := srv.Upload(context.Background(), filename, bytes.NewReader(tarGz)) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("illegal file path")) + }) + }) + Describe("extractVersion", func() { // extractVersion is unexported; we test via Upload with different filenames and tar layouts It("parses version from VMware-vix-disklib-X.Y.Z-... filename", func() {
Vulnerability mechanics
Root cause
"The tarball extraction logic did not properly resolve symlinks before writing files, allowing path traversal."
Attack vector
An unauthenticated attacker on the same local area network can send a specially crafted gzipped tarball to the assisted-migration-agent. This tarball can contain chained symlinks that bypass lexical path validation. By exploiting this, the attacker can write arbitrary files to the system, potentially leading to unauthorized code execution [ref_id=1].
Affected code
The vulnerability resides in the `extractTarGz` function within `internal/services/vddk.go`. Specifically, the logic for handling tar entries before writing them to disk failed to account for symlinks that could be used to traverse directories.
What the fix does
The patch modifies the `extractTarGz` function to resolve parent directory symlinks using `filepath.EvalSymlinks` before creating new files or directories. It then verifies that the resolved path remains within the intended destination directory. This prevents chained symlink attacks where a symlink points to a parent directory, allowing subsequent entries to write files outside the extraction target [patch_id=5487292].
Preconditions
- authThe attacker must be unauthenticated.
- networkThe attacker must be on the same local area network (LAN) as the vulnerable agent.
- inputThe attacker must be able to send a specially crafted gzipped tarball.
Generated on Jun 10, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.