Zoom out

This vulnerability in the Macintosh client for the Zoom audio/video conferencing system has gotten a lot of attention since it was published yesterday. In a nutshell:

  1. Safari forces users to click a button to confirm they want an app launched.1 This would mean an extra click for Zoom users.
  2. To get around this terrible imposition on their users, Zoom installed a webserver on their computers without telling them. A webserver that remains present and running, even when the Zoom app is not running. Even if you uninstall the Zoom app.
  3. There is a set of commands you can run to kill the webserver and prevent its relaunch, even if you still want/need to use Zoom. The commands are near the end of the Medium post by Jonathan Leitschuh, who discovered all this. Ironically, the commands sometimes don’t appear in Safari on the Mac, apparently because of some incompatibility between the Safari and the GitHub embedding code used in the post.

I don’t pretend to follow all of Leitschuh’s explanation of the vulnerability, but I do understand the commands for the fix. I thought I’d talk about what they do. Also, there’s a cut-and-paste solution getting some traction on Twitter that I want to talk about.

First, here’s what Zoom says about the installation of the webserver:

Second, when Zoom is installed on a Mac device by the user, a limited-functionality web server that can only respond to requests from the local machine is also installed on the device. This is a workaround to a change introduced in Safari 12 that requires a user to confirm that they want to start the Zoom client prior to joining every meeting. The local web server enables users to avoid this extra click before joining every meeting. We feel that this is a legitimate solution to a poor user experience problem, enabling our users to have faster, one-click-to-join meetings. We are not alone among video conferencing providers in implementing this solution.

There’s no question that Mac users have a tendency to complain about changes Apple makes to the Mac’s OS and first-party software (I may have even done some complaining myself). But generally speaking, we think Apple’s focus on security is a good thing, and we certainly don’t want apps installing servers on our computers without telling us about it. Even if it means the “poor user experience” of (horrors!) an extra click when we want to use video. Note that Zoom doesn’t discuss the poor user experience of finding your computer’s camera turned on when you visit a malicious website.

Assuming you want to keep Zoom on your computer, the first step in Leitschuh’s fix is to make sure Zoom’s “Turn off my video when joining a meeting” setting is clicked on:

Zoom video settings

Let us bask for a moment in the warm glow of a user interface that asks you to turn something on in order to turn something off. And remember, Zoom is very concerned about poor user experiences.

With that bit of GUI work done, we have to move to the command line to rid ourselves of the unwanted webserver. These steps will be needed even if you deleted the Zoom app.

The unwanted webserver uses port 19421. We can find which process is using that port through the lsof command:

lsof -i :19421

lsof means “list open files,” and it’s using the expansive Unix definition of a file, which is basically anything that can read or write a stream of bytes. An internet address that’s listening for input, which our webserver uses, fits within that definition. The -i option to lsof allows us to specify as much of an internet address as necessary. We could, for example, give the protocol (TCP) and host (localhost) along with the port, but in this case only the port number (which has to come after a colon) is necessary.

The output is a table of processes that fit the specification

COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ZoomOpene 59742 drang   7u  IPv4 0x31175d99fb28ad5b      0t0  TCP localhost:19421 (LISTEN)

The header line tells us what’s in the columns below. In this case, there’s only one entry. You can see from the COMMAND that it’s related to Zoom (the full name of the command is trucated to fit) and from the USER that it’s running as me. The key item is the PID, or process identification number. That’s the ID of the webserver we want to stop.

Stopping a process in Unix means using the kill command. Despite its name, kill normally just sends a signal to the given process to “please finish up what you’re doing and then stop, if you don’t mind.” But you can give it an option to be more forceful. The -9 option is aggressive; the process can’t ignore it and shuts down immediately. So our next command is

kill -9 <PID>

where <PID> is number we got from the lsof output.

This tweet from Mateusz Stawecki uses a Unix pipeline to get the PID from the lsof command and feed it to the kill command.

Stawecki tweet

Given that the point of this exercise is to enhance the security of our computers, it’s hard to justify copying and pasting a command from Twitter. But Stawecki’s command does work. Let’s see why.

Ignoring for a moment everything that comes after the first semicolon, the pipeline is

lsof -i TCP:19421 | awk 'NR > 1 {print $2}' | xargs kill -9

As we’ve seen, the TCP part of the lsof command is an unnecessary but harmless addition. The output of lsof is fed to the awk command, which skips over the header line (NR > 1) and outputs the second field (print $2) of the next line. As we’ve seen, this second field is the PID of the process we want to kill. The xargs command takes the PID and turns it into an argument for the kill command.

There’s an easier way to do this, eliminating the awk command entirely. lsof has a “terse” option, -t, which reduces the output to just the PID(s) of the matching process(es). So we can cut Stawecki’s pipeline down to just two steps:

lsof -ti :19421 | xargs kill -9

And since there’s only one matching process, we could reduce the command even further:

kill -9 $(lsof -ti :19421)

The $(command) construct says “run this command and put its output here.” Because I like building up my commands step by step, I prefer the xargs solution, but either will work.

Now we’ve killed the web server, but we need to do more to prevent it from coming back. Zoom put a folder named .zoomus in your home directory, and inside that folder is an app called ZoomOpener. This is the app that launches the web server.

ZoomOpener app

We get rid of the app and its enclosing folder through

rm -r ~/.zoomus

Leitschuh’s article and Stawecki’s pipeline both use rm -rf, but the force (-f) option is unnecessary. The Unix rm command is powerful and a little scary, especially when combined with the recursive (-r) option. You may prefer to use the Finder to throw the .zoomus folder into the Trash and empty it. If so, you’ll have make the folder visible (files and folders that start with a period are invisible by default). This is done by pressing ⇧⌘. (that’s Command-Shift-Period). Press it again to go back to the usual Finder view.

With the ZoomOpener app deleted, we must be finished, right? Wrong. The Zoom app will recreate the folder and the ZoomOpener app the next time you launch it unless you do this:

touch ~/.zoomus

The touch command is meant to update an existing file’s access and modification dates without changing its contents, but if the file you give it as an argument doesn’t exist, it will create it. For reasons I don’t quite understand, putting a file named .zoomus in your home directory prevents the Zoom app from creating a .zoomus folder and installing ZoomOpener in it.

If you go back to Stawecki’s tweet, you’ll see that he has the rm and touch commands in there after the pipeline. Again, I don’t blame you for being leery of using his commands, but they do work and won’t ruin your system.

Of course, the real question is why should you trust me? Or anyone? You may want to read Ken Thompson’sReflections on Trusting Trust.”


  1. Thanks to Jeff Johnson for clearing up my initial confusion about the confirmation. Jeff makes StopTheNews, an app that handles Apple News URLs and is the sort of app that gets launched from a URL and requires a confirmation. Pretty sure Jeff hasn’t added a web server to get around that.