Security Concern | macOS app not signed, related privileged and continuous root execution

Dear Zerotier team,

I couldn’t find a relevant existing issue so I’m starting this thread.

I love ZT feature and architecture but I’m concerned about the way the macOS client gets installed via the packaged.

The issue is not the package itself (which is also signed) but rather the fact that the final installed app is not correctly signed. In specific the app creates a series of login items that are from Unidentified Developer.

What is more disturbing is the fact that some of these items are registered under root and nobody UUID.

What is even more disturbing is the fact that zerotier is always running as root rather than in a sandboxed way. While I may understand while the client/agent needs to act as root to modify networking, I am particularly concerned of having zerotier-one always running as root in the background, even when the UI has been quitted and not tunnel is running.

This is to me a very important security concern that needs to be addressed. Also, it shouldn’t be too hard as there is already a very nicely crafted iOS / iPadOS app that seems to work very well. Would it be so hard to notarize and distribute Zerotier One via the Apple Store at least for Apple Silicon? Or at least via a notarized app that behaves more likely to wireguard (with the latter, if no tunnel nor UI is running, no wg process is present in the background).

Thanks in advance for the work!

All executables in the Installer package for ZeroTier One are signed in accordance with Apple’s policies

$ codesign -v -v -R="notarized" MacEthernetTapAgent
MacEthernetTapAgent: valid on disk
MacEthernetTapAgent: satisfies its Designated Requirement
MacEthernetTapAgent: explicit requirement satisfied

$ codesign -v -v -R="notarized" zerotier-one
zerotier-one: valid on disk
zerotier-one: satisfies its Designated Requirement
zerotier-one: explicit requirement satisfied

$ codesign -v -v -R="notarized" /Applications/ZeroTier.app
/Applications/ZeroTier.app: valid on disk
/Applications/ZeroTier.app: satisfies its Designated Requirement
/Applications/ZeroTier.app: explicit requirement satisfied

As to the scripts. They’re there for utility purposes. Simply being owned by root does not make them a security issue. If any of the calls in the script requires root access to execute, the user executing it will need root/sudo access before they will be executed anyway.

The launch item is from LaunchDaemon

$ cat /Library/Application\ Support/ZeroTier/One/launch.sh
#!/bin/bash
export PATH="/Library/Application Support/ZeroTier/One:/bin:/usr/bin:/sbin:/usr/sbin"
/usr/bin/killall MacEthernetTapAgent >>/dev/null 2>&1
exec zerotier-one

Created from the launchd config file:

cat /Library/LaunchDaemons/com.zerotier.one.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>com.zerotier.one</string>
	<key>UserName</key>
	<string>root</string>
	<key>ProgramArguments</key>
	<array>
		<string>/Library/Application Support/ZeroTier/One/launch.sh</string>
	</array>
	<key>WorkingDirectory</key>
	<string>/Library/Application Support/ZeroTier/One</string>
	<key>StandardOutPath</key>
	<string>/dev/null</string>
	<key>StandardErrorPath</key>
	<string>/dev/null</string>
	<key>KeepAlive</key>
	<true/>
</dict>
</plist>

While perhaps not ideal, I’m unaware of any ways to sign shell scripts on macOS. Apple does not require nor enforce that shell scripts be signed either.

As to ZeroTier One always running as root: as you suspect, it must run as root to modify network settings on the machine. It also runs as root/administrator on *BSD, and Windows for the same reasons. On Linux it’s initially launched as root but Linux has systems in place for dropping privileges. Unfortunately, macOS has no such ability so we cannot do any more while still keeping access to modify network settings.

The UI itself does not run as root, but it’s also just an interface to the service API running on the zerotier-one background agent. Quitting the UI does not stop the background agent and this is by design.

Using a method similar to the iOS/ipadOS apps is less than desirable as well as Apple’s NetworkExtension framework is extraordinarily limited. We use it on iOS and iPadOS because we have no other choice, but it also prevents a lot of features from being used on them; multicast, connecting to multiple networks simultaneously, L2 bridging, amongst others. So while it may technically be possible to use the NetworkExtension framework on macOS, users would loose many features they’ve come to rely on over the years.

If your curious, we did have a protocol audit done by Trail of Bits a few years back. You can find the audit in their GitHub Repo. You can also find our Security issue reporting policy here. In the 10+ years ZeroTier has been around, there has never been an exploit reported against the background agent.

1 Like

Thank you very much for the extensive reply. I believe this might also help other user in the future.

I wasn’t aware of the strong limitation of the NetworkExtension framework, now it makes sense.

Thanks also for pointing out about the app signing. What sparked this conversation is indeed the shell script missing signature:

❯ sfltool dumpbtm
========================
 Records for UID -2 : (redacted)
========================

 ServiceManagement migrated: true
 SharedFileList migrated: false

 Items:

 #1:
                 UUID: (redacted)
                 Name: (null)
       Developer Name: (null)
                 Type: developer (0x20)
          Disposition: [disabled, allowed, visible, not notified] (2)
           Identifier: Unknown Developer
                  URL: (null)
           Generation: 0
  Embedded Item Identifiers:
    #1: com.zerotier.one

 #2:
                 UUID: (redacted)
                 Name: launch.sh
       Developer Name: (null)
                 Type: legacy daemon (0x10010)
          Disposition: [enabled, allowed, visible, notified] (11)
           Identifier: com.zerotier.one
                  URL: file:///Library/LaunchDaemons/com.zerotier.one.plist
      Executable Path: /Library/Application Support/ZeroTier/One/launch.sh
           Generation: 1
    Parent Identifier: Unknown Developer


========================
 Records for UID 0 : (redacted)
========================

 ServiceManagement migrated: false
 SharedFileList migrated: false

 Items:

 #1:
                 UUID: (redacted)
                 Name: (null)
       Developer Name: (null)
                 Type: developer (0x20)
          Disposition: [disabled, allowed, visible, not notified] (2)
           Identifier: Unknown Developer
                  URL: (null)
           Generation: 1


========================
 Records for UID 248 : (redacted)
========================

 ServiceManagement migrated: true
 SharedFileList migrated: false

 Items:


========================
 Records for UID 501 : (redacted)
========================

 ServiceManagement migrated: true
 SharedFileList migrated: true

 Items:

 #1:
                 UUID: (redacted)
                 Name: (null)
       Developer Name: (null)
                 Type: developer (0x20)
          Disposition: [disabled, allowed, visible, not notified] (2)
           Identifier: Unknown Developer
                  URL: (null)
           Generation: 0
  Embedded Item Identifiers:
    #1: com.zerotier.one

which the results in this:

I understand this might be superfluous if not an exercise of style. Although I was aurally reading this.

Hope it helps and thanks again for the prompt reply.