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.
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).
CVE-2026-5674 Severity
CVE-2026-5674 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
How to Reproduce CVE-2026-5674
pruva-verify REPRO-2026-00309 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 Proof of Reproduction for CVE-2026-5674
- 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
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
- PulseAudio unix socket
- pulse-server.c do_load_module()
- module-ladspa-sink
- filter-chain LADSPA loader dlopen(<absolute path>) in the out-of-sandbox pipewire-pulse daemon
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for CVE-2026-5674
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-pulsesession 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-pulsedaemon 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
src/modules/module-protocol-pulse/pulse-server.c—do_load_module()(line 5051) handlesCOMMAND_LOAD_MODULEfrom the PulseAudio native protocol. Its only guard isif (!impl->defs.allow_module_loading) return -EACCES;. The property defaults to true (/usr/share/pipewire/pipewire-pulse.conf:#pulse.allow-module-loading = true).src/modules/module-protocol-pulse/modules/module-ladspa-sink.c— the registered pulse modulemodule-ladspa-sinkacceptsplugin=andlabel=arguments and builds a filter.graph node{ type = ladspa plugin = "<plugin>" label = "<label>" }, loaded viapw_context_load_module(..., "libpipewire-module-filter-chain", ...)inside thepipewire-pulseprocess.spa/plugins/filter-graph/plugin_ladspa.c—load_ladspa_plugin(): when the plugin string starts with/, it is passed directly toladspa_handle_load_by_path(), which callsdlopen(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 subsequentdlsym("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 makesdo_load_module()return-EACCES("Failure: Access denied") — used as this run's negative control.
Reproduction Steps
bundle/repro/reproduction_steps.sh(self-contained; exit 0 = confirmed).- What it does:
- Builds the attacker library
shared/evil.so(constructor writesCVE-2026-5674-PWNED pid=... uid=... comm=...into the host-only oracle dir; the oracle path is delivered via the daemon'sORACLE_PATHenvironment 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-pulsedaemons on the "host" side with a freshXDG_RUNTIME_DIR; health-checks viapactl info. - Runs the attacker client inside
bwrap --unshare-user --unshare-ipc --unshare-uts --unshare-cgroupwith only: the PulseAudio runtime dir, the writable shared dir (containingevil.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>.txtwhose pid equals the livepipewire-pulsepid and whose comm ispipewire-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).
- Builds the attacker library
- Expected evidence: markers
logs/marker_vuln_attempt{1,2}.txt, client logs showingORACLE_PATH_NOT_VISIBLE+ LOAD_MODULE, fixed attempts showingFailure: 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-pulsemarker 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}.log—Failure: 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: daemonuser:[4026533197], sandboxed clientsuser:[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 = falseinpipewire-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-loadingto false; ideally the pulse server should apply Flatpak-style client confinement awareness (as PulseAudio'sclient.accessmodel starts to do) before honoringLOAD_MODULE. - Testing recommendations: regression test that
pactl load-module module-ladspa-sink plugin=/tmp/x.sofails 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_NEWNSeven 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-pidwas omitted because a fresh/proccannot be mounted inside rootless Docker (locked procfs mounts); the ticket requires mount+user namespace separation, which is provided and evidenced./proc/self/nsis bind-mounted read-only into the sandbox solely for identity evidence.- Edge case: the daemon answers
Failure: Missing implementationfor the malicious module becauseevil.soexports noladspa_descriptor; code execution already happened duringdlopen()(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.
Unknown error
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.jsonEXPLOIT-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.
Unknown error
Artifacts and Evidence for CVE-2026-5674
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-5674
FAQ: CVE-2026-5674
Is CVE-2026-5674 exploitable?
How severe is CVE-2026-5674?
How can I reproduce CVE-2026-5674?
Is the CVE-2026-5674 reproduction verified?
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.