https://bugs.kde.org/show_bug.cgi?id=523067

            Bug ID: 523067
           Summary: Valgrind dsymutil Command Injection via Crafted
                    Library Path
    Classification: Developer tools
           Product: valgrind
      Version First unspecified
       Reported In:
          Platform: Other
                OS: All
            Status: REPORTED
          Severity: major
          Priority: NOR
         Component: general
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

Created attachment 194306
  --> https://bugs.kde.org/attachment.cgi?id=194306&action=edit
poc

# Valgrind dsymutil Command Injection via Crafted Library Path

**Product:** Valgrind
**Version:** 3.27.1 (latest release)
**File:** `coregrind/m_debuginfo/readmacho.c`
**Function:** `ML_(read_macho_debug_info)` (line 1338-1346)
**CWE:** CWE-78 (Improper Neutralization of Special Elements used in an OS
Command)
**CVSS 3.1:** 7.8 (AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)
**Auth Required:** None (user runs program under Valgrind)
**Validation: Lab verified**
**Environment:** Valgrind 3.27.1 source (valgrind-3.27.1.tar.bz2), macOS,
default build configuration
**Commit:** HEAD at sourceware.org/git/valgrind.git (verified 2026-07-01)

## Summary

When Valgrind processes a Mach-O binary on macOS that has no matching dSYM
debug symbols, it invokes `/usr/bin/dsymutil` via `VG_(system)` (which calls
`/bin/sh -c <cmd>`). The library filename is embedded in double quotes, which
do not prevent shell command substitution (`$()` or backticks). An attacker who
controls a library path can achieve arbitrary command execution when a user
runs any program that loads that library under Valgrind.

The `--dsymutil` flag defaults to `yes`, making this reachable in all default
macOS Valgrind configurations.

## Vulnerable Code

`coregrind/m_debuginfo/readmacho.c`, lines 1332-1346:

```c
/* Run dsymutil */

{ Int r;
  const HChar* dsymutil = "/usr/bin/dsymutil ";
  HChar* cmd = ML_(dinfo_zalloc)( "di.readmacho.tmp1",
                                  VG_(strlen)(dsymutil)
                                  + VG_(strlen)(di->fsm.filename)
                                  + 32 /* misc */ );
  VG_(strcpy)(cmd, dsymutil);
  if (0) VG_(strcat)(cmd, "--verbose ");
  VG_(strcat)(cmd, "\"");
  VG_(strcat)(cmd, di->fsm.filename);   // <-- unsanitized filename
  VG_(strcat)(cmd, "\"");
  if (VG_(clo_verbosity) > 0) {
     VG_(message)(Vg_DebugMsg, "run: %s\n", cmd);
  }
  r = VG_(system)( cmd );               // <-- executes via /bin/sh -c
```

`VG_(system)` implementation at `coregrind/m_libcproc.c:549`:

```c
const HChar *argv[4] = { "/bin/sh", "-c", cmd, 0 };
pid = VG_(spawn)(argv[0], argv);
```

## Data Flow

1. User runs `valgrind ./program` on macOS
2. `./program` loads a shared library (dylib) from a path controlled by the
attacker
3. Valgrind's debug info reader processes the library via
`ML_(read_macho_debug_info)`
4. If no matching dSYM directory is found and the path is not a "system" path
(not under `/usr/`, `/bin/`, `/System/`, `/Library/`, `/Applications/`,
`/opt/`, `/sw/`, `/sbin/`), Valgrind proceeds to run dsymutil
5. Line 1318: `if (!VG_(clo_dsymutil))` -- defaults to `True` (enabled), so
this block is skipped
6. Lines 1338-1342: The library filename is concatenated into a shell command
with only double-quote protection
7. Line 1346: `VG_(system)(cmd)` invokes `/bin/sh -c "/usr/bin/dsymutil
\"<filename>\""`
8. In POSIX shell, double quotes do NOT prevent `$()` command substitution or
backtick expansion

## Attack Model

An attacker creates a shared library at a path containing shell metacharacters:

```
/tmp/lib$(touch${IFS}/tmp/pwned).dylib
```

Or using backticks:

```
/tmp/lib`touch /tmp/pwned`.dylib
```

When a program linked against this library (or one that `dlopen()`s it) is run
under Valgrind:

1. Valgrind detects the Mach-O shared library
2. No dSYM file exists at the expected path
3. Path is not in the system library exclusion list (not under `/usr/`,
`/System/`, etc.)
4. Valgrind constructs: `/usr/bin/dsymutil
"/tmp/lib$(touch${IFS}/tmp/pwned).dylib"`
5. `/bin/sh -c` expands `$(touch${IFS}/tmp/pwned)` before dsymutil runs
6. Arbitrary command execution in the context of the Valgrind process

**Vectors for placing the malicious library:**
- DYLD_LIBRARY_PATH or DYLD_INSERT_LIBRARIES manipulation
- Shared library in a user-writable project directory (e.g., `./lib/`)
- Crafted application bundle with embedded dylibs
- CTF challenges or untrusted binaries with embedded library references

**Default exposure:** All macOS Valgrind users. The `--dsymutil=yes` default
means no special flags are needed.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to