Tech9 min read

Testing OBS Browser Source Permissions and Disabled CEF Sandbox on Windows

IkesanContents
  • Tested on: OBS Studio 32.2.2 (current stable) and 33.0.0-rc3 (release candidate) on Windows 11
  • Core risk: The impact is governed by browser source permissions, obs-websocket authentication, and the combination of outdated Chromium with a disabled CEF sandbox
  • Page permissions: Even at Level 5 (Full Access), malicious scripts are confined to OBS API calls (start stream, switch scenes) and cannot read arbitrary local files or execute OS commands
  • obs-websocket risk: If enabled without password authentication, arbitrary Web pages can connect to port 4455 and extract stream keys via GetStreamServiceSettings
  • Host execution chain: All CEF sub-processes run with --no-sandbox. Combining overlay XSS with unpatched V8 vulnerabilities (like CVE-2024-7971) can lead to native code execution on the host PC

I heard a rumor claiming that “OBS Studio can be compromised through PHP, and high browser source permissions let a tampered PHP site take over OBS.”

However, browser sources only render and execute client-side HTML and JavaScript. Wondering how server-side PHP could possibly play a role and what operations actually pass through each permission level, I unpacked portable builds of OBS Studio on a Windows 11 PC to test the actual behavior directly.

Test Environment

I extracted two official portable releases of OBS Studio into separate directories on Windows 11, leaving the main system installation untouched.

VersionEmbedded Chrome/CEF (Measured)Role
OBS 32.2.2127.0.6533.120Current stable release
OBS 33.0.0-rc3150.0.7871.252Release candidate with updated CEF

The embedded Chrome/CEF (Chromium Embedded Framework) version was verified directly from the strings compiled inside libcef.dll.

For testing, I pointed the browser source to a local diagnostic HTML page. This page checks what functions exist inside window.obsstudio (the control object injected by OBS), tests which API calls succeed under each permission level, and attempts to access local files (file://) and the local OBS control socket (ws://127.0.0.1:4455). Dangerous actions like actually starting a stream were omitted to verify API availability safely.

Browser Sources and the PHP Misconception

An OBS browser source runs Chromium (CEF) internally, rendering standard HTML, CSS, and JavaScript. PHP executes exclusively on the Web server before sending output to the client. By the time content reaches OBS, it is already rendered HTML and JavaScript. The test page’s User-Agent string was Chrome/127.0.6533.120 OBS/32.2.2, and it revealed nothing about server-side technologies.

The rumor likely confuses this with WordPress or PHP-based sites frequently targeted for defacement or script injection. If an attacker compromises a Web server and injects malicious JavaScript, that script executes inside the viewer’s OBS browser source. This behavior is identical whether the hosting server runs PHP, Node.js, or static HTML.

How Page Permissions Work

OBS browser sources have a “Page permissions” setting that regulates what JavaScript running inside the page can control within OBS. Newly created sources default to Level 1 (“Access to OBS status information”).

LevelNamePermitted Operations
0NoneRead permission level only
1ReadObs (Default)Check streaming/recording status
2ReadUserList scenes, get current scene and transition
3BasicSave replay buffer
4AdvancedStart/stop replay, switch scenes and transitions
5AllStart/stop streaming, recording, and virtual camera

This API exposes no methods to read stream keys, extract OBS configuration files, inspect local file systems, or execute shell commands. Even with maximum permissions, a compromised page is strictly confined to the functions listed above.

The default property dialog assigns Level 1 access:

OBS browser source properties showing default Level 1 permissions

Users can manually escalate this to Level 5 (“Full access to OBS”), which displays a built-in security warning:

Escalating page permissions to Full Access with security warning

Measured Behavior Across Permission Levels (OBS 32.2.2)

I compared API call results between Level 1 (default) and Level 5 (Full Access). Port 4455 is the default port used by obs-websocket.

Test Action from PageL1 ReadObs (Default)L5 Full Access
window.obsstudio injectedYesYes
Enumerate function namesAll visible (including startStreaming)All visible
Granted control level getControlLevel15
Get stream/record status getStatusSuccessSuccess
Get scene info getCurrentScenenull (Denied)Success (Scene 1920x1080)
Switch scene setCurrentSceneDeniedSuccess (tested with current scene name)
Read local file file://FailedFailed
Control socket ws://127.0.0.1:4455UnreachableUnreachable

While function names can be enumerated under all levels, actual execution is strictly enforced. Level 1 only permits status checks; scene queries and switching are rejected. At Level 5, scene querying and switching succeed, and stream/recording triggers become callable.

The diagnostic output rendered inside OBS under Level 5 permissions confirms these boundaries:

Diagnostic page running under Level 5 permissions inside OBS

Local file reads and control socket connections remained blocked even under Level 5. These capabilities fall outside the browser source API entirely. Granting Full Access to a compromised overlay allows an attacker to stop your stream, start unwanted recording, or switch scenes. However, it does not directly grant access to system files or host execution.

Chromium Sandbox Configuration in Subprocesses

The host-level compromise stems from the Chromium sandbox configuration. OBS launches Chromium renderer processes to handle page content, but running them without a sandbox removes the barrier that prevents an exploited renderer from accessing the host operating system.

Examining the launch arguments of running OBS 32.2.2 browser subprocesses confirms the sandbox state:

SubprocessSandbox Argument
renderer x2 (runs page JavaScript)--no-sandbox
gpu-process x1--no-sandbox
utility x2--no-sandbox

Every child process had --no-sandbox explicitly appended, matching the source code configuration (settings.no_sandbox = true). The renderer processes executing untrusted JavaScript run without OS-level sandboxing protection.

The Real-World Attack Chain Demonstrated on OBS 32.2.2

In September 2024, Orange Cyberdefense Switzerland (SCRT) published a proof-of-concept demonstrating Remote Code Execution (RCE) against OBS Studio 32.2.2.

The attack chain worked across three stages:

  1. Overlay XSS: A custom chat overlay dynamically injected raw viewer chat messages into the HTML DOM without sanitization (innerHTML).
  2. V8 Type Confusion: The injected JavaScript exploited CVE-2024-7971, a high-severity type confusion bug in Chromium’s V8 engine.
  3. Disabled Sandbox Escape: Because OBS ran Chromium with --no-sandbox, the V8 memory corruption directly compromised the host process and executed arbitrary code on the streamer’s PC without requiring a separate sandbox escape.

Google patched CVE-2024-7971 in Chrome 128 in August 2024, but OBS 32.2.2 was still bundled with Chromium 127.

Page permissions played no role in this exploit. The attack vector depended solely on an unsanitized overlay paired with an outdated, unsandboxed browser engine.

Connecting to obs-websocket from Browser Sources

obs-websocket allows external applications (such as Elgato Stream Deck) to remotely control OBS over WebSockets on port 4455. It is disabled by default. I enabled the server to test whether a Web page loaded in a browser source can reach it.

Test Action from PageBefore EnablingAfter Enabling
Connect to ws://127.0.0.1:4455FailedConnected
HTTP request to http://127.0.0.1:4455FailedResponse received
Read local file file://FailedFailed

Once enabled, a browser source page from any Web origin could immediately open a WebSocket connection to 127.0.0.1:4455. The server does not reject connections based on Web origin headers.

The obs-websocket server dialog includes options for server toggling and password configuration:

obs-websocket server settings enabled with port 4455 and password authentication

However, establishing a socket connection does not automatically grant execution rights:

Authentication SettingSocket Behavior
Password enabled (Default)Connection opens, but requests are rejected until authenticated with the correct password
Authentication disabledThe socket immediately accepts unauthenticated commands from the page

Running obs-websocket without authentication is particularly dangerous. Under the obs-websocket v5 protocol, the GetStreamServiceSettings request returns the complete streaming configuration, including destination RTMP URLs and secret stream keys. An unauthenticated socket allows any compromised overlay to harvest stream keys and issue arbitrary control commands.

Reading Scope When Using “Local file”

OBS browser sources have a “Local file” setting that lets users load an HTML file directly from disk rather than a remote URL. I evaluated what files can be read under this setting.

When “Local file” is selected, the loaded document receives an origin of http://absolute rather than file://. OBS maps local directory contents under this virtual HTTP origin.

Read Target from PageResult
File in same directory (relative path)Success
System file (file:///C:/Windows/win.ini)Failed
Absolute path on disk (file:// absolute URL)Failed

The diagnostic test loaded via Local File demonstrates this boundary:

Diagnostic test loaded via Local File setting

A local file overlay can read adjacent files in its own directory via relative paths (./data.json). However, direct references to file:///C:/Windows/... fail because browser security blocks cross-scheme navigation from http://absolute to file://.

Access remains confined to the overlay’s folder. Furthermore, a remote Web page cannot programmatically toggle the “Local file” checkbox inside OBS properties.

Verification on OBS 33.0.0-rc3 (CEF 150)

I repeated the test suite on OBS 33.0.0-rc3, which upgrades CEF to version 150.0.7871.252.

Permission Level 5 behavior remained unchanged from 32.2.2:

Test Action from PageOBS 32.2.2 (L5)OBS 33.0.0-rc3 (L5)
Granted control level getControlLevel55
Get stream/record status getStatusSuccessSuccess
Get scene info getCurrentSceneSuccessSuccess (Scene 1920x1080)
Switch scene setCurrentSceneSuccessSuccess
Read local file file://FailedFailed

The diagnostic output in 33.0.0-rc3 confirms the Chrome 150 User-Agent while maintaining the same security boundaries:

OBS 33.0.0-rc3 browser source running Chrome 150 under Level 5

Subprocess arguments confirmed that --no-sandbox is still applied across all processes in 33.0.0-rc3:

SubprocessOBS 32.2.2OBS 33.0.0-rc3
renderer x2--no-sandbox--no-sandbox
gpu-process x1--no-sandbox--no-sandbox
utility x2--no-sandbox--no-sandbox

Upgrading to CEF 150 incorporates modern Chromium security fixes and patches known vulnerabilities like CVE-2024-7971. However, the sandbox remains disabled by design, meaning any unpatched vulnerability in CEF could follow the same execution path.

Practical Defenses for Streamers

  • Sanitize custom chat overlays: Never inject dynamic viewer text into the DOM using innerHTML. Treat user messages strictly as text via textContent to eliminate XSS entry points.
  • Do not load untrusted remote URLs: Avoid embedding third-party overlay URLs from unverified or insecure servers.
  • Keep page permissions minimal: Maintain the default Level 1 setting unless an overlay specifically requires scene or stream control.
  • Always require obs-websocket authentication: Never disable password protection on port 4455 to prevent unauthorized local API access from web sources.
  • Keep OBS Studio updated: Apply updates promptly to ensure your embedded CEF browser includes the latest V8 security patches.