Skip to content

CVE-2026-5674: Verified Reproduction

CVE-2026-5674: PipeWire sandbox escape via malicious library loading in PulseAudio compatibility layer

CVE-2026-5674 is verified against the affected target. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00309.

REPRO-2026-00309 Jul 28, 2026 CVE entry ↗ .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
88m 8s
Tool calls
286
Spend
$9.12
01 · Overview

What Is CVE-2026-5674?

CVE-2026-5674 is a high-severity vulnerability. Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00309).

02 · Severity & CVSS

CVE-2026-5674 Severity

CVE-2026-5674 is rated high severity.

HIGH threat level

High — serious impact or readily exploitable. Prioritize remediation.

How to Reproduce CVE-2026-5674

$ pruva-verify REPRO-2026-00309
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00309/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-5674

sandbox escape — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

absolute LADSPA plugin path and label passed via the PulseAudio native protocol LOAD_MODULE command (pactl load-module module-ladspa-sink plugin=<path> label=<x>) from a bubblewrap-confined client

Attack chain
  1. PulseAudio unix socket
  2. pulse-server.c do_load_module()
  3. module-ladspa-sink
  4. filter-chain LADSPA loader dlopen(<absolute path>) in the out-of-sandbox pipewire-pulse daemon
Runnable proof: reproduction_steps.sh
Captured evidence: client fixed attempt2client fixed attempt1daemon fixed attempt1 pipewiredaemon fixed attempt1 pulsedaemon fixed attempt2 pipewiredaemon fixed attempt2 pulse
How the agent worked 662 events · 286 tool calls · 1h 28m
1h 28mDuration
286Tool calls
122Reasoning steps
662Events
35Dead-ends
Agent activity over 1h 28m
Policy
1
Support
12
Repro
270
Judge
38
Variant
336
Verify
1
0:0087:58

Root Cause and Exploit Chain for CVE-2026-5674

Versions: verified on PipeWire 1.6.2 (Ubuntu 26.04, 1.6.2-1ubuntu1.1). The

native protocol LOAD_MODULE command. The request handler only checks the pulse.allow-module-loading server property (default: true) and then loads any of the built-in PulseAudio compatibility modules with fully attacker-controlled arguments. One of those modules, module-ladspa-sink, forwards the attacker-supplied plugin= argument to PipeWire's filter-chain LADSPA loader, which calls dlopen() on the value verbatim when it is an absolute path. Because Flatpak-style sandboxes deliberately expose the PulseAudio unix socket to sandboxed applications, an attacker confined in a bubblewrap/Flatpak sandbox can make the out-of-sandbox pipewire-pulse daemon dlopen() an attacker-controlled shared library, executing its ELF constructors in the daemon process and thereby escaping the sandbox.

(libpipewire-module-protocol-pulse, pulse module-ladspa-sink/module-ladspa-source, SPA filter-graph LADSPA plugin).

  • Affected versions: verified on PipeWire 1.6.2 (Ubuntu 26.04, 1.6.2-1ubuntu1.1). The Debian security tracker lists every release as vulnerable (bullseye 0.3.19 through sid 1.6.8); no upstream fix exists at the time of this run.
  • Risk level and consequences: Important (CVSS 8.8 per Amazon ALAS). Any sandboxed application with access to the PulseAudio socket (default for audio-playing Flatpaks) can execute arbitrary code in the user's unsandboxed pipewire-pulse session daemon, i.e. a full sandbox escape with the daemon's privileges.

Impact Parity

  • Disclosed/claimed maximum impact: sandbox escape — arbitrary code execution outside the sandbox (bubblewrap/Flatpak-style namespace sandbox) via the PulseAudio compatibility layer.
  • Reproduced impact from this run: attacker-controlled code executed in the host-side pipewire-pulse daemon from a client confined in a real bubblewrap sandbox with separate mount and user namespaces. Proof: the loaded library's constructor wrote fresh markers (CVE-2026-5674-PWNED pid=<daemon pid> uid=1000 comm=pipewire-pulse) into a host-only oracle directory that the sandboxed client could neither see nor write.
  • Parity: full.
  • Not demonstrated: nothing material — a constructor can contain arbitrary code; the marker write is representative attacker-controlled code execution in the daemon.

Root Cause

  1. src/modules/module-protocol-pulse/pulse-server.cdo_load_module() (line 5051) handles COMMAND_LOAD_MODULE from the PulseAudio native protocol. Its only guard is if (!impl->defs.allow_module_loading) return -EACCES;. The property defaults to true (/usr/share/pipewire/pipewire-pulse.conf: #pulse.allow-module-loading = true).
  2. src/modules/module-protocol-pulse/modules/module-ladspa-sink.c — the registered pulse module module-ladspa-sink accepts plugin= and label= arguments and builds a filter.graph node { type = ladspa plugin = "<plugin>" label = "<label>" }, loaded via pw_context_load_module(..., "libpipewire-module-filter-chain", ...) inside the pipewire-pulse process.
  3. spa/plugins/filter-graph/plugin_ladspa.cload_ladspa_plugin(): when the plugin string starts with /, it is passed directly to ladspa_handle_load_by_path(), which calls dlopen(path, RTLD_NOW) (line 196) with no allowlist, no confinement check, and no consideration of whether the requesting client is sandboxed. dlopen() executes the library's constructors, yielding arbitrary code execution in the daemon. (The subsequent dlsym("ladspa_descriptor") failure only rejects the module after attacker code has already run.)
  • Fix commit: none upstream at the time of this run (Debian tracker: "unfixed"). The disclosed mitigation is pulse.allow-module-loading = false, which makes do_load_module() return -EACCES ("Failure: Access denied") — used as this run's negative control.

Reproduction Steps

  1. bundle/repro/reproduction_steps.sh (self-contained; exit 0 = confirmed).
  2. What it does:
    • Builds the attacker library shared/evil.so (constructor writes CVE-2026-5674-PWNED pid=... uid=... comm=... into the host-only oracle dir; the oracle path is delivered via the daemon's ORACLE_PATH environment variable).
    • If user namespaces are blocked by the outer container seccomp (they are on this worker: unshare(CLONE_NEWUSER) → EPERM even for root), it re-executes itself inside a Docker container (--security-opt seccomp=unconfined, ubuntu:26.04 with pipewire/pipewire-pulse/pulseaudio-utils/bubblewrap) as unprivileged uid 1000.
    • Starts the real pipewire + pipewire-pulse daemons on the "host" side with a fresh XDG_RUNTIME_DIR; health-checks via pactl info.
    • Runs the attacker client inside bwrap --unshare-user --unshare-ipc --unshare-uts --unshare-cgroup with only: the PulseAudio runtime dir, the writable shared dir (containing evil.so), and read-only system binds. The host-only oracle directory is not bound.
    • The client proves it cannot see the oracle path and that a probe write never reaches the host, then issues the real protocol request pactl load-module module-ladspa-sink sink_name=pwnsink plugin=<abs path> label=pwn.
    • Host verifies a marker file marker_<daemon pid>.txt whose pid equals the live pipewire-pulse pid and whose comm is pipewire-pulse; namespace identity of client vs daemon is recorded both from inside the sandbox and from the host.
    • Two vulnerable attempts against fresh daemon processes, then two negative controls with pulse.allow-module-loading=false (request denied, no marker).
  3. Expected evidence: markers logs/marker_vuln_attempt{1,2}.txt, client logs showing ORACLE_PATH_NOT_VISIBLE + LOAD_MODULE, fixed attempts showing Failure: Access denied, RESULT: vuln_ok=2/2 fixed_ok=2/2, exit 0.

Evidence

  • bundle/logs/reproduction_steps.log — full orchestration log; key lines:
    • marker present: CVE-2026-5674-PWNED pid=37 uid=1000 comm=pipewire-pulse
    • marker pid/comm match live daemon (pid=37 comm=pipewire-pulse)
    • client runs in a different user namespace (user=user:[4026533274] vs daemon user:[4026533197])
    • RESULT: vuln_ok=2/2 fixed_ok=2/2 / CVE-2026-5674 CONFIRMED (two consecutive runs)
  • bundle/logs/client_vuln_attempt{1,2}.log — sandboxed client: oracle not visible, probe write does not reach host, pactl load-module module-ladspa-sink plugin=...evil.so.
  • bundle/logs/marker_vuln_attempt{1,2}.txt — host-only markers with live daemon pid/comm.
  • bundle/logs/client_fixed_attempt{1,2}.logFailure: Access denied; bundle/logs/negative_control_obs_fixed_attempt{1,2}.json — structured observation.
  • bundle/logs/ns_evidence_*.txt / ns_client_host_*.txt — namespace/process identity: daemon user:[4026533197], sandboxed clients user:[4026533274/...275], distinct mount namespaces, observed both from inside the sandbox and from the host.
  • bundle/repro/runtime_manifest.json — runtime evidence manifest (all flags true).
  • Environment: Ubuntu 26.04 (worker) / ubuntu:26.04 (experiment container), pipewire and pipewire-pulse 1.6.2-1ubuntu1.1, pactl 17.0, bubblewrap, kernel 7.0.14-arch1-1.

Recommendations / Next Steps

  • Mitigation (per disclosure): set pulse.allow-module-loading = false in pipewire-pulse.conf (validated as this run's negative control).
  • Suggested upstream fix: refuse absolute-path LADSPA plugins for pulse-protocol clients (or only allow plugins from trusted system directories), and/or default pulse.allow-module-loading to false; ideally the pulse server should apply Flatpak-style client confinement awareness (as PulseAudio's client.access model starts to do) before honoring LOAD_MODULE.
  • Testing recommendations: regression test that pactl load-module module-ladspa-sink plugin=/tmp/x.so fails on hardened configs; CI sandbox test mirroring this script.

Additional Notes

  • Idempotency: the script is fully idempotent — it rebuilds the payload, regenerates configs, uses fresh runtime dirs per attempt, cleans oracle markers per attempt, and kills its daemons on exit. It passed three consecutive end-to-end runs on this worker.
  • Outer-container limitation: the worker's seccomp blocks CLONE_NEWUSER/CLONE_NEWNS even for root, so the experiment runs inside a Docker sibling container with an unconfined seccomp profile. Inside it, the bubblewrap sandbox uses genuine, separate mount and user namespaces (verified via namespace inode evidence from both sides); the pipewire-pulse daemon runs outside that sandbox. This matches the ticket's required bubblewrap/Flatpak-style confinement (chroot was not used).
  • --unshare-pid was omitted because a fresh /proc cannot be mounted inside rootless Docker (locked procfs mounts); the ticket requires mount+user namespace separation, which is provided and evidenced. /proc/self/ns is bind-mounted read-only into the sandbox solely for identity evidence.
  • Edge case: the daemon answers Failure: Missing implementation for the malicious module because evil.so exports no ladspa_descriptor; code execution already happened during dlopen() (constructor), which is exactly why the marker exists despite the error.

CVE-2026-5674 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:003:57
0:00
session startedaccounts/fireworks/models/kimi-k3 · CVE-2026-5674 · REPRO-20
0:16
0:17
web search
0:18
web search
0:31
0:32
1:23
1:23
extract_facts
no facts extracted
1:25
1:25
supportclaim_contract
1:51
1:51
1:51
2:12
2:12
2:12
2:25
2:25
error

Unknown error

2:25
2:38
2:54
2:54
3:11
$jq '{snapshot_sha256: .snapshot_sha256, results: [.results[] | {record_id: .record.record_id // .record_id, title: .record.body.title // .title, summary: (.record.body.summary // .summary)[0:200]}]}' /workspace/bundle/learning/exploit_knowledge_preflight.json 2>/dev/null || jq 'keys' /workspace/bundle/learning/exploit_knowledge_preflight.json
0.2s
EXPLOIT-KNOWLEDGE PREFLIGHT REVIEW REQUIRED: `run_shell` is blocked until this agent calls `review_exploit_knowledge_preflight` with snapshot_sha256 `0f0d836dddad6b042a6477f95870fa546654b3db23a68bc5c5dfdd466d2fd7cd` and exactly one disposition for each candidate ID [202870db-2d8e-49c3-b1b6-c0d55f8d75b8, bb9f16f0-cf8f-4718-90db-9b1d8593981f]. Historical records remain reference-only; review their typed mechanisms and preconditions before continuing.
3:12
error

Unknown error

3:35
3:35
3:57
08 · How to Fix

How to Fix CVE-2026-5674

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-5674 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-5674

Is CVE-2026-5674 exploitable?

Yes. Pruva independently reproduced CVE-2026-5674 and verified the exploit fires end-to-end in a sandboxed environment. A runnable proof-of-concept script and the full agent transcript are on this page (reproduction REPRO-2026-00309).

How severe is CVE-2026-5674?

CVE-2026-5674 is rated high severity.

How can I reproduce CVE-2026-5674?

Pruva provides a verified reproduction script on this page. Download it and run it inside an isolated environment such as a container or virtual machine — never against production. The reproduction was confirmed end-to-end by Pruva's automated agents.

Is the CVE-2026-5674 reproduction verified?

Yes. Pruva reproduced CVE-2026-5674 with high confidence in a sandboxed environment, capturing the full agent transcript and artifacts as evidence.
11 · References

References for CVE-2026-5674

Authoritative sources for CVE-2026-5674 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.