VYPR
High severity8.1OSV Advisory· Published Sep 5, 2025· Updated Apr 19, 2026

CVE-2025-9566

CVE-2025-9566

Description

There's a vulnerability in podman where an attacker may use the kube play command to overwrite host files when the kube file container a Secrete or a ConfigMap volume mount and such volume contains a symbolic link to a host file path. In a successful attack, the attacker can only control the target file to be overwritten but not the content to be written into the file.

Binary-Affected: podman Upstream-version-introduced: v4.0.0 Upstream-version-fixed: v5.6.1

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/containers/podman/v5Go
< 5.6.15.6.1
github.com/containers/podman/v4Go
<= 4.9.5

Affected products

1

Patches

1
43fbde4e665f

kube play: don't follow volume symlinks onto the host

https://github.com/containers/podmanPaul HolzingerAug 29, 2025via ghsa
4 files changed · +71 4
  • pkg/domain/infra/abi/play.go+2 3 modified
    @@ -810,8 +810,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
     			defaultMode := v.DefaultMode
     			// Create files and add data to the volume mountpoint based on the Items in the volume
     			for k, v := range v.Items {
    -				dataPath := filepath.Join(mountPoint, k)
    -				f, err := os.Create(dataPath)
    +				f, err := openPathSafely(mountPoint, k)
     				if err != nil {
     					return nil, nil, fmt.Errorf("cannot create file %q at volume mountpoint %q: %w", k, mountPoint, err)
     				}
    @@ -821,7 +820,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
     					return nil, nil, err
     				}
     				// Set file permissions
    -				if err := os.Chmod(f.Name(), os.FileMode(defaultMode)); err != nil {
    +				if err := f.Chmod(os.FileMode(defaultMode)); err != nil {
     					return nil, nil, err
     				}
     			}
    
  • pkg/domain/infra/abi/play_linux.go+18 0 added
    @@ -0,0 +1,18 @@
    +//go:build !remote
    +
    +package abi
    +
    +import (
    +	"os"
    +
    +	securejoin "github.com/cyphar/filepath-securejoin"
    +)
    +
    +// openSymlinkPath opens the path under root using securejoin.OpenatInRoot().
    +func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
    +	file, err := securejoin.OpenatInRoot(root, unsafePath)
    +	if err != nil {
    +		return nil, err
    +	}
    +	return securejoin.Reopen(file, flags)
    +}
    
  • pkg/domain/infra/abi/play_unsupported.go+13 0 added
    @@ -0,0 +1,13 @@
    +//go:build !linux && !remote
    +
    +package abi
    +
    +import (
    +	"errors"
    +	"os"
    +)
    +
    +// openSymlinkPath is not supported on this platform.
    +func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
    +	return nil, errors.New("cannot safely open symlink on this platform")
    +}
    
  • pkg/domain/infra/abi/play_utils.go+38 1 modified
    @@ -2,7 +2,14 @@
     
     package abi
     
    -import "github.com/containers/podman/v5/libpod/define"
    +import (
    +	"fmt"
    +	"os"
    +	"strings"
    +
    +	"github.com/containers/podman/v5/libpod/define"
    +	"golang.org/x/sys/unix"
    +)
     
     // getSdNotifyMode returns the `sdNotifyAnnotation/$name` for the specified
     // name. If name is empty, it'll only look for `sdNotifyAnnotation`.
    @@ -16,3 +23,33 @@ func getSdNotifyMode(annotations map[string]string, name string) (string, error)
     	}
     	return mode, define.ValidateSdNotifyMode(mode)
     }
    +
    +// openPathSafely opens the given name under the trusted root path, the unsafeName
    +// must be a single path component and not contain "/".
    +// The resulting path will be opened or created if it does not exists.
    +// Following of symlink is done within staying under root, escapes outsides
    +// of root are not allowed and prevent.
    +//
    +// This custom function is needed because securejoin.SecureJoin() is not race safe
    +// and the volume might be mounted in another container that could swap in a symlink
    +// after the function ahs run. securejoin.OpenInRoot() doesn't work either because
    +// it cannot create files and doesn't work on freebsd.
    +func openPathSafely(root, unsafeName string) (*os.File, error) {
    +	if strings.Contains(unsafeName, "/") {
    +		return nil, fmt.Errorf("name %q must not contain path separator", unsafeName)
    +	}
    +	fdDir, err := os.OpenFile(root, unix.O_RDONLY, 0)
    +	if err != nil {
    +		return nil, err
    +	}
    +	defer fdDir.Close()
    +	flags := unix.O_CREAT | unix.O_WRONLY | unix.O_TRUNC | unix.O_CLOEXEC
    +	fd, err := unix.Openat(int(fdDir.Fd()), unsafeName, flags|unix.O_NOFOLLOW, 0o644)
    +	if err == nil {
    +		return os.NewFile(uintptr(fd), unsafeName), nil
    +	}
    +	if err == unix.ELOOP {
    +		return openSymlinkPath(fdDir, unsafeName, flags)
    +	}
    +	return nil, &os.PathError{Op: "openat", Path: unsafeName, Err: err}
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

32

News mentions

0

No linked articles in our index yet.