Little Snitch is a macOS app and network filter extension made by Objective Development Software. I purchased Little Snitch years ago, continue to use it, and will continue to use it. I've also done a lot to promote Little Snitch on my blog and even in the news media. However, this blog post raises a privacy issue with Little Snitch that bothers me, and I believe the public has the right to know about it. My hope is that highlighting this issue will prompt some improvements in Little Snitch to ameliorate it.
Here's Objective Development's advertisement for the Alert Mode of Little Snitch:
Whenever an app attempts to connect to a server on the Internet, Little Snitch shows a connection alert, allowing you to decide whether to allow or deny the connection. No data is transmitted without your consent. Your decision will be remembered and applied automatically in the future.
When you look at the implementation of Little Snitch, the interpretation of the word "data" becomes crucial. Technically, unless you allow the connection, Little Snitch does indeed prevent HTTP data from getting sent. Nonetheless, Little Snitch does not prevent TCP (Transmission Control Protocol) data from getting sent. This TCP data includes your IP address, which can often be used to personally identify you. The server knows that you, i.e., your IP address, tried to connect to the server, even when Little Snitch "denies" the connection.
The background to this blog post is that I was testing whether Little Snitch exempts its own connections to the developer's server from getting blocked by Little Snitch. I already knew that Little Snitch could block its own software update, but I wanted to see whether there was anything else, ironically in order to assuage someone else's fear about the app. The good news is that Little Snitch doesn't exempt its own connections, as far as I can tell. I tested this on one of my Macs that didn't yet have Little Snitch installed. I disconnected the Mac from the internet, installed Little Snitch, and went through the entire setup process. Below is a minimal set of rules that you can use for testing. It blocks everything except
configd, which is required to connect to your router, and
mDNSResponder, which is required to perform DNS queries. (By the way, the "Information from Objective Development" in the screenshot below was available without an internet connection, so it appears to be bundled with Little Snitch.)
I ran the
tcpdump command in Terminal to record a packet trace, and then I reconnected my Mac to the internet to see what happens.
In the packet trace, I found no connections to Objective Development. Disturbingly, though, there was some unusual TCP activity, for example to Apple! (The IP addresses were in Apple's allocated 184.108.40.206/8 IPv4 range.) At first I suspected that Apple had exempted itself from network extension filtering again. However, when I emailed Objective Development, I learned the truth: Little Snitch allows some TCP packets to be sent. Objective Development told me that Little Snitch uses deep packet inspection to try to get a name for the connection. I assume they meant for example the HTTP Host header, the HTTP/2 :authority pseudo-header, or the TLS Server Name Indication. The IP address alone would not necessarily provide a unique domain name, because multiple domains can use the same IP address.
An HTTP connection over TCP has to initiate a 3-step "handshake" before any actual data—such as HTTP headers—can be sent over the connection. Every TCP packet, including any packet involved in the handshake, contains the IP addresses of the sender and the receiver. Thus, before Little Snitch can perform deep packet inspection, the IP address of your Mac may have already been sent to the remote server!
You can test the Little Snitch IP address leak yourself. Just run the following command from Terminal app. (See
man tcpdump for a description of the options.)
The information leak appears to occur for every TCP connection, as far as I can tell.
While researching my blog post, I discovered a blog post from 2021 by Rhino Security labs titled Bypassing Little Snitch Firewall with Empty TCP Packets. For some reason, their blog post didn't seem to attract any public attention at the time; I certainly wasn't aware of it back then. The blog post described a technique, along with a proof of concept, to connect to a server without triggering a Little Snitch alert dialog. The author discovered the same unsettling fact that I've now discovered:
Despite what it might seem, Little Snitch alerting doesn’t trigger at the first TCP packet, but rather waits until application data is sent before interrupting the connection and alerting the user. This is to support its domain-to-IP connection features.
The author also provided a disclosure timeline, listing email exchanges with Objective Development:
Nov 3, 2021 – Objective Development responded with a detailed email on why this behavior is necessary for Little Snitch and why it cannot be fixed.
I haven't tested whether the proof of concept, which has both client and server components, still works on the latest version of Little Snitch. Strangely, the author never mentioned the privacy implications of Little Snitch's behavior, the fact that TCP packets with your IP address are allowed to be sent to the server without your consent. Perhaps the author was focused exclusively on the possibility of a malicious app bypassing the Little Snitch alert dialog, whereas my concern is simply personal privacy.
I understand that Objective Development is in a difficult position here, because the Little Snitch feature of allowing or denying connections by domain name may sometimes require deep packet inspection. The issue, in my opinion, is that Little Snitch seems to always require deep packet inspection. I don't understand why it's required when a rule for a particular process is specified to "Deny any outgoing connection". The domain name doesn't matter in this case. I have a lot of Little Snitch rules like that, denying all connections for a process that I don't want to connect to the internet at all.
I'd like to see an explicit option in Little Snitch rules to enable or disable deep packet inspection. I should be able to opt out of the behavior. As an alternative, Little Snitch could use the DNS cache to correlate the IP address with the domain. After all, any domain-based connection by necessity has already queried DNS to find the IP address of the server. In many cases, this correlation between IP address and domain will be unique. And if there happen to be multiple domains in the DNS cache with the same IP address, then Little Snitch could show its alert and ask me whether to allow or deny the connection, without allowing my IP address to be leaked to the server.
At the very least, I'd like to see Objective Development publicly acknowledge this issue, not just privately in emails to the technically sophisticated who happen to discover it on their own. Until now, I've trusted Little Snitch to protect my privacy, but my trust has been a bit shaken. Although Little Snitch is still an extremely useful tool, there's an important gap between what I believed it did and what it actually does. Objective Development knew all along, while their customers, myself included, were largely oblivious.
LuLu is an open source firewall for outgoing network connections, made by security researcher Patrick Wardle. I decided to test it briefly, and I found the same kind of IP address leak as Little Snitch, perhaps for the same reason. However, I'm not 100% confident in my results, because as a Little Snitch user, I'm much less familiar with LuLu and how it works. Moreover, the feature set and user interface of LuLu seem, let's just say, primitive compared to Little Snitch.
See my follow-up post.