Foreword

This vulnerability allows any website to forcibly join a user to a Zoom call, with their video camera activated, without the user's permission.

On top of this, this vulnerability would have allowed any webpage to DOS (Denial of Service) a Mac by repeatedly joining a user to an invalid call.

Additionally, if you’ve ever installed the Zoom client and then uninstalled it, you still have a localhost web server on your machine that will happily re-install the Zoom client for you, without requiring any user interaction on your behalf besides visiting a webpage. This re-install ‘feature’ continues to work to this day.

Yep, no joke.

This vulnerability leverages the amazingly simple Zoom feature where you can just send anyone a meeting link (for example https://zoom.us/j/492468757 ) and when they open that link in their browser their Zoom client is magically opened on their local machine. I was curious about how this amazing bit of functionality was implemented and how it had been implemented securely. Come to find out, it really hadn’t been implemented securely. Nor can I figure out a good way to do this that doesn’t require an additional bit of user interaction to be secure.

This vulnerability was originally responsibly disclosed on March 26, 2019. This initial report included a proposed description of a ‘quick fix’ Zoom could have implemented by simply changing their server logic. It took Zoom 10 days to confirm the vulnerability. The first actual meeting about how the vulnerability would be patched occurred on June 11th, 2019, only 18 days before the end of the 90-day public disclosure deadline. During this meeting, the details of the vulnerability were confirmed and Zoom’s planned solution was discussed. However, I was very easily able to spot and describe bypasses in their planned fix. At this point, Zoom was left with 18 days to resolve the vulnerability. On June 24th after 90 days of waiting, the last day before the public disclosure deadline, I discovered that Zoom had only implemented the ‘quick fix’ solution originally suggested.

Ultimately, Zoom failed at quickly confirming that the reported vulnerability actually existed and they failed at having a fix to the issue delivered to customers in a timely manner. An organization of this profile and with such a large user base should have been more proactive in protecting their users from attack.

Timeline

Mar 8, 2019 — Requested security contact via Twitter (no response).

Mar 26, 2019 — Contacted Zoom Inc via email with 90-day public disclosure deadline. Offered a “quick fix” solution.

Mar 27, 2019

- Requested confirmation of reception.

- Informed that Zoom Security Engineer was Out of Office.

- Offered and declined a financial bounty for the report due to policy on not being able to publicly disclose even after the vulnerability was patched.

- Requested confirmation of reception. - Informed that Zoom Security Engineer was Out of Office. - Offered and declined a financial bounty for the report due to policy on not being able to publicly disclose even after the vulnerability was patched. Apr 1, 2019 — Requested confirmation of vulnerability.

Apr 5, 2019 — Response from Zoom Security Engineer confirming and discussing severity. Settled on CVSSv3 score of 5.4/10.

Apr 10, 2019 — Vulnerability disclosed to Chromium security team.

Apr 18, 2019 — Updated Zoom with the suggestion from Chromium team.

Apr 19, 2019 — Vulnerability disclosed to Mozilla FireFox security team.

Apr 26, 2019 — Video call with Mozilla and Zoom Security Teams

Disclosed details of impending DNS expiration.

Disclosed details of impending DNS expiration. June 7, 2019 —Email from Zoom about a video call to discuss fix.

June 11, 2019 — Video call with Zoom Security team about impending disclosure. Discussed how Zoom’s planned patch was incomplete.

June 20, 2019 — Contacted about having another video call with Zoom Security Team. Declined by me due to calendar conflicts.

June 21, 2019 — Zoom reports vulnerability was fixed.

June 24, 2019 — 90-day public disclosure deadline ends. Vulnerability confirmed fixed with ‘quick fix’ solution.

July 7, 2019 — Regression in the fix causes the video camera vulnerability to work again.

July 8, 2019

- Regression fixed.

- Workaround discovered & disclosed.

- Public Disclosure.

Details

On Mac, if you have ever installed Zoom, there is a web server on your local machine running on port 19421 . You can confirm this server is present by running lsof -i :19421 in your terminal.

First off, let me start off by saying having an installed app that is running a web server on my local machine with a totally undocumented API feels incredibly sketchy to me. Secondly, the fact that any website that I visit can interact with this web server running on my machine is a huge red flag for me as a Security Researcher.

Here’s the code on the Zoom site that tipped me off to this localhost server.

My original thoughts when I learned that this web server existed was that if there is a buffer overflow anywhere in the parameter handling of this web server, someone could achieve RCE on my machine. That’s not what I’ve found, but that was my original thought process.

If you look at what’s logged to the web developer console when visiting one of those Zoom ‘join’ links, you should see something like this:

Browser console logs when visiting https://zoom.us/j/492468757

I also found that, instead of making a regular AJAX request, this page instead loads an image from the Zoom web server that is locally running. The different dimensions of the image dictate the error/status code of the server. You can see that case-switch logic here.

The two numbers are the pixel dimensions of the image returned by the web server.

The scary thing is that this enum seemed to indicate that this web server can do far more than just launch a Zoom meeting. What I found out was that this web server can also re-install the Zoom app if a user has uninstalled it, more on that later.

One question I asked is, why is this web server returning this data encoded in the dimensions of an image file? The reason is, it’s done to bypass Cross-Origin Resource Sharing (CORS). For very intentional reasons, the browser explicitly ignores any CORS policy for servers running on localhost.

I’m guessing that this is intentionally done for security reasons. Regardless, it seems that Zoom is abusing a hack to bypass CORS protection. More on that later.

The Video Call Vulnerability

I created a personal meeting with a different account and cracked open Postman and started to remove parameters to see what the minimal GET request was that was required to launch a Zoom meeting.

There are a lot of random parameters that are sent to the localhost web server but the only ones that seem to matter are the following.

action=join

confno=[whatever the conference number is]

Using the following GET request from Postman I was successfully able to get my computer to join the Zoom call that the other account had created.

Once I had this, I started tinkering with what other “action” parameters I might be able to pass to get the client to do other things. I wasn’t able to find anything even after searching through the various public documents and the public ProtoBuff schema for hints about what hidden functionality may exist. Again, this webserver’s API is completely undocumented as far as I can tell and I’ve spent several hours searching for any mention of this Desktop web server in the official and unofficial documentation.

So now I had a minimal POC that I could use to maliciously get any user into a call, however, the default setting for the “New Meeting” is to allow the user to choose whether to join their Audio/Video. I would consider this alone a security vulnerability.