Open mitchcapper opened 2 years ago
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.
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.
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.
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.
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.
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
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?
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.
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:
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 https://github.com/obsproject/obs-websocket/issues/424#issuecomment-763240944 pointed out some of the very real security issues that exist with obs (when this plugin is enabled).
There are several relevant points:
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.