Skip to content

Security Issue: Allow changing of binding options to decrease attack vectors #907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
mitchcapper opened this issue Feb 8, 2022 · 10 comments

Comments

@mitchcapper
Copy link

Operating System Info

Other

Other OS

No response

OBS Studio Version

Other

OBS Studio Version (Other)

No response

obs-websocket Version

Git

OBS Studio Log URL

n/a

OBS Studio Crash Log URL

No response

Expected Behavior

n/a

Current Behavior

n/a

Steps to Reproduce

/na

Anything else we should know?

Before the details I figured start with why this matters, what is the attack surface this plugin exposes and what is at risk if a malicious user (you below) can successfully execute commands:

  • You can create new scenes/captures of the users entire desktop for example, and then start a remote stream or save the recording remotely.
  • You can write files to the local disk at arbitrary paths you specify (save screenshot for example)
  • You can remotely listen to any audio input (microphone) or output on the users pc
  • Trigger absolutely any OBS hot key the user has setup
  • Read the users configuration for scenes/sources/profiles/stream services/etc (not sure if this could have sensitive information embedded but it looks like it could contain keys)
  • Read images/videos from anywhere on the users computer. You do not need to know the name of the image/video you want to see just using the slideshow functionality you can read entire directories of images/videos. For example trying common paths for storage/sync services like google drive, dropbox, onedrive, or just the users Pictures and Videos folder in their private account.
  • Read other files from the computer, I have no idea on OBS's full capabilities but I at least see a "Text Source" option which I believe can easily access any UTF-8 encoded file.
  • I don't see an obvious 'execute random OS command or application' but OBS supports such a massive source of inputs and outputs I might be missing the easy method of doing this.
  • OBS does not run in a lower security context, a sandbox, or drop the user's credential tokens (I believe) so likely anything the user has network access to is also fair game (ie a user on a corporate network having all file share access available).
  • The fact: All code contains bugs, some bugs can lead to remote code execution / full system access, unmanaged languages like C/C++ (what this plugin is written in) are more likely to be subject to corruption/off by one exploits. You have rolled your own security / web socket authentication method (sadly a downside of there not being a standard), meaning less eyes are probably on that code than more standardized code. You have to allow the user to connect and issue certain commands before you get to authentication, so there is already more potential vulnerability (and parsing of arbitrary json or MsgPack binary data that the client sends) than say a TLS client cert auth stack.

The above could also contain errors (or miss other things), as I am not really an obs user and just briefly looked at the remote API as part of something else. Some of the above may be possible to enable and disable before the user only noticed. For continuous monitoring the only way a user may notice is the red dot in the OBS systray icon (which windows hides by default) or randomly checking the OBS window. I did notice the 5x plugin added a windows toast option on initial connect (and client list, but rarely would someone look at the client list). It looks like toast is off by default (although even with it unchecked on my new install I still get the toasts so not sure).

You are about to become embedded in every OBS install by default (although I assume/hope off by default). This by no means is about removing the above functionality but hopefully reducing the attack surface for these things may be available through.

Now for the details of the ticket:

Sorry I don't actually use OBS but from some quick googling I didn't see another avenue for reporting security issues. I was casually looking at another project that uses this plugin and noticed the global binding by default. I also saw @bdrung in #778 made a simple enough request, allow binding the web socket to localhost. @tt2468 shot this down saying they didn't see much use for this, there is password auth and websites can abuse even local only sockets with their own code. Something else that might have helped was #54 which was deemed unneeded as password by default was added (although that feature served another purpose). Ironically @notr1ch in #424 (comment) pointed out some of the very real security issues that exist with obs (when this plugin is enabled).

There are several relevant points:

  • Security is about layers and you largely relying on a single one, a password
  • If a security feature can narrow an attack surface even if not completely eliminating it, it should be considered. With notice to potential false senses of security (oh I bind to the local IP so no password access is just fine).
  • Browsers do pose a very real security risk to internal web servers this is why more security restrictions are coming: https://developer.chrome.com/blog/private-network-access-update/ / https://wicg.github.io/private-network-access/ still if this is the only likely vulnerable path that is still a massive footprint reduction from anyone with network access
  • Password required, was NOT the default until a year ago. In addition from the notes on the 5 beta it seems it may not have always been set to non-empty when this change happened. If some of these other features were used, users may have been far less vulnerable (or still are if they have not upgraded or potentially are not running the beta)
  • Given the list of 3rd party apps that you post planning to work with 5.0 I can only imagine a whole lot more use this product. Some of them may encourage or require users to use this plugin without a password. While you cannot stop users (or 3rd party apps) potentially from requiring stupid things you can still reduce that attack footprint.
  • In Please support running websocket on localhost #778 they said hey, just block it at a firewall level. Fair enough, but this is very advanced and 99% of your users won't know how to do this. Default windows users normally have one chance to obviously do this, that is on first startup with this plugin enabled, however almost all users will click allow access here without understanding what they are allowing. For one, as Windows Defender doesn't know what the required access is for, the user has virtually no information to make an informed decision even if they tried. It just says "Windows defender firewall has blocked some features of OBS Studio on public and private networks" do you want to allow?. Given OBS is often used for streaming I highly doubt most users would ever say no to that.

Suggestions:

  • For browsers give an option if users want to allow them. With the upcoming changes which will require " Access-Control-Allow-Private-Network" when network boundaries are crossed: https://developer.chrome.com/blog/private-network-access-preflight/#what-is-private-network-access / https://web.dev/cors-rfc1918-feedback/ sadly the first chrome restrictions will not completely cutoff local access according to the specs above, but it is in theory coming. In the mean time X-XSS-Protection block, and reject WS requests with Origin headers that are not a localhost alias are decent methods to block browsers. I think blocking browsers would need to be an option not a always, as I believe some of the apps that use the web socket are browser hosted, but the local origin requirements likely work.

  • For the love of god allow users to bind to localhost only, it is a simple checkbox you need to add (or better default to on) and massively reduces the attack surface. Otherwise a user on a random public wifi network (or god forbid a publicly routed computer) are instant targets. It also likely satisfies a large portion of your users needs without restricting them.

  • Randomize the port for the socket when the password is randomized. You are going to use the QR code or have to remotely configure your apps at that point anyway, might as well enter port number then too. While this is more obfuscation, it greatly increases the time to scan large amounts of IP's for OBS instances.

  • There are near 0 conditions I can imagine this websocket should ever be accessed through a router. It is not a secure web socket, there is no certificate options, if you are using a proxy infront of it that does not count as routed through a router (in most normal setups). Please take advantage of boost::asio::socket_base::do_not_route option(true); if you really have a use case to have routed packets give the user a checkbox for allowing public routed access to the web socket but again, probably a bad idea as users don't often know what they are doing.

  • There are a slew of more complicated additions of any large footprint API system like allowing scoped api access (I may need my phone to be able to change to existing scenes remotely but not create entirely new scenes or save random things to my hard drive). Per client approval on initial connect, suggested in one of the bug reports above (you don't need to fingerprint the client just issue them back a secret token to identify themselves on future connections, this also gives the user an opportunity to name that connection at initial creation). For browser based clients another option here is to force the web socket connect request to include a ticket/token that it first obtains with a normal HTTP request to the server. This has the great benefit of allowing all the standard cross site scripting header/request prevention mechanisms to work that don't work over the web socket.

  • Consider requiring a startup parameter for really insecure setups, just like you don't allow the debug command to be stuck on, show a message box on every startup of users who do something like an unauthenticated web socket with public routing enabled unless a command line arg is passed (--web-socket-allow-complete-unathed-access-to-my-files anyone;)). Might be helpful to let users know that anyone with web socket abilities can read/write their files and likely capture them remotely (also might be a nice warning when the user is setting the socket password). Another security improvement is requiring passwords (or change the terminology to say an API key) and only allow it to be randomly generated (with a regenerate button). Users are bad at picking passwords.

  • Given how vulnerable old versions might be and now being an in-house plugin, a warning on detected old insecure versions may be a good idea.

Some of these suggestions are breaking changes, and users clients would potentially stop working (ie for randomized port if retroactively applied). The great news is you are coming up on V5 that is ALREADY going to break things, so take advantage. IE Rather than require the use of port 4455 randomize it at upgrade.

Clearly do what you like with this information / suggestions I am no expert here, just offering some incite that might be useful.

@tt2468
Copy link
Member

tt2468 commented Jun 8, 2022

Reopening this as we decided to revert the previously committed solution out of caution for user experience.

The going idea right now is to implement a token system for clients, where new clients can optionally (default) be required to be manually approved by the user before they can be allowed. obs-websocket would provide a token upon successful authentication, which the client would save, then pass it to obs-websocket along with correct authentication details to avoid popups in the future.

Basically, a default-enabled feature that warns the user when an unrecognized client connects, but not after subsequent connects. Changing the WebSocket server password would invalidate all tokens.

@tt2468 tt2468 reopened this Jun 8, 2022
@root-goblin
Copy link

root-goblin commented Sep 26, 2022

Would it be reasonable keep both the default as open to external connections, and also retain the option to close external connections (serve over localhost only). This would prevent breakage for users that depend on the existing behavior, but also keep open an upgrade path for the ecosystem to slowly move toward secure-by-default.

Edit: this has already been pointed out as a solution in the original issue

For the love of god allow users to bind to localhost only, it is a simple checkbox you need to add (or better default to on) and massively reduces the attack surface. Otherwise a user on a random public wifi network (or god forbid a publicly routed computer) are instant targets. It also likely satisfies a large portion of your users needs without restricting them.

@mitchcapper
Copy link
Author

OBS can act as massive spyware for anyone who can remote control it. Breaking insecure remote connections to warn the user probably is a more reasonable solution (at a minimum users without a password set). Keep in mind that is also only remote connections which I would have a hard time figuring is more than a very small fraction of the user base. Without local bind by default most users are far more exposed than they likely know.

@JorianWoltjer
Copy link

This issue still seems to be an active problem. I found this same vulnerability independently and also failed to find a security contact until I came across this GitHub issue.
I would like to add that it is possible to reliably get Remote Code Execution on a victim who visits a malicious public site (eg. attacker.com) all in the background. This is a big risk for the small percentage of people using the 'No authentication' setting.

I am happy to disclose the full code execution method privately but won't post it here publicly yet. I think the research is useful to others so I will publicize it later, hopefully after OBS has fixed the issue.

@mitchcapper
Copy link
Author

mitchcapper commented Sep 21, 2024

This issue still seems to be an active problem.

Yes they were going to partially fix this but, as often the case, security lost to convenience.

I would like to add that it is possible to reliably get Remote Code Execution

Yes as well, OBS is crazy powerful in terms of the myriad of ways you can get output/input interact with other processes (including using triggers), and spy on the user all before you even consider 3rd party plugins.

They should have sucked it up and said in version X we are going to require tokens (either through an updated token system or even older apps have been given tokens through the password interface). Then for that version a welcome on first launch saying hey you have remote access enabled we are going to restrict it to the localhost interface by default and non-updated apps will need to manually have a token password input into them and updated apps you will just have to hit approve on.

It is a pain but the web socket is part of the official OBS now and has massive holes that allow for some crazy user abuse.

It is going to be those spam emails come true, I have been watching the sites you visit and have you on camera using your own software, and their tip at the end will be 'enable a password and local only bind in OBS for the future' hahaha.

@mitchcapper
Copy link
Author

Also, the chrome changes that would have helped prevent some of the exploit vectors if we set the right headers got pushed way back. What was going to be in shortly in 2022, is still not a thing in 2024:
https://wicg.github.io/private-network-access/

chromes latest update (some things happened in the 130 just released but still quite rolled back): https://developer.chrome.com/blog/private-network-access-update-2024-03

@FlyingFathead
Copy link

I just stumbled upon OBS's WebSocket server and am genuinely baffled that binding to 127.0.0.1 (local loopback) by default hasn't been implemented yet.

This is an egregious oversight and a significant security risk. By default, the WebSocket server should only listen on the local loopback (127.0.0.1), with options to change this for other use cases.

Like others have noted, allowing it to bind to all interfaces (0.0.0.0) by default is reckless and exposes users to unnecessary risks, especially given how powerful OBS is as a potential attack vector.

The fact that there is no way to change the bind address natively (short of duct-taping a solution with iptables, nginx, etc.) is unacceptable.

This needs to be patched as a priority.

What were the developers thinking when this decision was made?

@mitchcapper
Copy link
Author

mitchcapper commented Nov 19, 2024

The fact that there is no way to change the bind address natively

I don't use OBS but my understanding was that you should be able to change the bind address just not defaulted to localhost.

What were the developers thinking when this decision was made?

The same thing everywhere always, convenience trumps security.

What is sad is that when I filed this ticket V5 was going to be a breaking release and would have been the perfect time to enable additional security. At a minimum they could have enabled token based approval for new installs.

@Fenrirthviti
Copy link
Member

Just as a status update, we are aware of and acknowledge the risk here.

By default, all websocket configurations in OBS have a randomly generated password, and a user must take very explicit steps to either disable that or provide authentication details to an attacking actor. This significantly reduces the attack vector, and lowers the overall risk of an attack to be fairly minimal.

That said, I am in full agreement this is something that should be changed, as the vast majority of users will only be using local websocket clients, and those needing to allow remote connections can be provided configuration options in some fashion that are not readily exposed.

As a final note, this is very likely to be a change that is accepted, and PRs are welcome as the project only has a handful of full-time developers it's not clear when we will have time to prioritize this. As mentioned, the risk is fairly low. It might seem like a simple/easy change, but adding another configuration option is another point of failure and another thing we need to test and ultimately provide maintenance for. Assistance is very welcome here.

@mitchcapper
Copy link
Author

@Fenrirthviti good granted 3 years later but:) One item to note, the fact OBS (at least according to the other comments, I don't have it installed to check) still doesn't have the ability to allow the user to do localhost binding at least as an option is silly. You literally already wrote the code, just don't default it to checked if you want to have 100% compatibility for existing users but at least offer the option. Here is the code the obs team added then reverted: 1cd12c1 I would also still strongly suggest forcing bind local for any users without a password set for obvious reasons.

The other change I made (and would still recommend) that would be low overhead would be to change the terminology to say password/api key and not allow the user to manually set it but randomly generate it (you can leave existing passwords as they are just make the text box read/copy only with a regen button like most api services).

Finally, one of the most near free things, provide a clear disclaimer on what the user is allowing something like: "Enabling web sockets allows full remote control of OBS and all plugins, anyone with this API key can easily record audio and your screen, and potentially execute code or access files on your system. Please note that you should only use OBS web sockets remotely on secure networks, while the password is not transmitted in clear text all other data is."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants