TunnelBear is quickly becoming one of the most popular options for “normal” people to use VPNs. It’s easy to use and the heavily targeted, yet non-obnoxious marketing doesn’t hurt either. Plus I have an irrational affection for brands that combine awesome products with a cute front-end. The same reason I’m in love with Neat microphones.
TunnelBear is cheap and non-intrusive, but not open-source. The account management happens on their servers, using unique user tokens. So the client is not all that critical, and doesn’t provide much attack surface. But attacking TB’s service is not the point of this post.
That said, the client is after all a proprietary piece of software that runs on your PC, managing the most private part of your communication: your internet connection. So it’s important to know what makes it tick, and what it knows and shares about you.
TunnelBear has yet to have a major fracas, and (spoilers), I didn’t find any in my analysis. Yet there might be certain parts of this software that may surprise you.
TunnelBear targets the broader, “average Joe” audience, so my analysis is focused on the Windows client. Saying “TunnelBear is not open-source” isn’t quite true. First of all it uses FOSS components, and it’s written in C#, which, if you’re not familiar with reverse engineering, is the same thing as having a neatly formatted and optimized copy of the full source code — minus the comments.
The TunnelBear base installation copies some 165 files onto your PC. The separate parts of the software are
So quite heavy. But that’s what you need to close every possible leak on Windows. If you just want to proxy your browser, something like TorGateway is more than enough. But not every app honors the internet options, so other connections still expose your actual IP. You need to proxy everything.
The app is launched by this hilarious script:
Set WshShell = CreateObject(“WScript.Shell”)cmds=WshShell.RUN(“TBear.Client.exe”, 0, True)Set WshShell = Nothing
So let’s take a look at our entry point, TBear.Client. I’m looking at version 2.3.29.0. There are multiple mentions of a version 3 in the code, and many checks to enable or disable updates to v3, more on that later. This assembly is linked to all the image, sound and font assets. It also contains the default English resources.
No bear glove = risky internet loveGrr. Your privates are showing…
etc. pp.
First off is the connection helper routine. It simply waits for an internet connection by using Utils.PingTunnelBear()
with a default maximum of 3 attempts. It pings www.tunnelbear.com
as a check.
The actual app is launched async. On startup, it first restores the default proxy. This applies to the “internet options” proxy settings withing windows. These are stored in Software\Microsoft\Windows\CurrentVersion\Internet Settings
as an address and a flag (on/off). It only resets the proxy address if the existing one is local, which is a good idea if you are already using another proxy.
It also makes sure to resume operation and to reconnect the VPN when the system power mode changes.
Now to the actual startup handler. This is the first time we encounter a curious flag called Utils.IsProd
. This flag controls a number of things. One of them being the server that is accessed by the client, which is tunnelbear.com for production, but staging.tunnelbear.com otherwise.
More importantly though, this disables or enables the frighteningly comprehensive logging-to-stdout that is baked into every routine:
Utils.IsGodModeUnlocked = false;if (!Utils.IsProd) {Logger.InitializeConsole();}
Another curious flag is the self-explanatory AutoUpdater.IsBeta
.
It then tests of the system (or the current user) is capable of running IntelliBear (the DNS proxy, remember). This is done by creating a dummy VPN instance on port 53. If that fails, the user probably doesn’t have permission or something is already running there.
Finally we get to parse the command line options. The following are available:
-lang
, for setting the UI language-autoconnect
, whether to auto connect on startup-tcp
, to force the VPN to use TCP-intellibear-error
, logs the last IB error ( -intellibear-info
logs the last info statement)-intellibear-verbose
forces IB into verbose mode.-reset
forces a reset-uitest mode
runs this UI test mode-autologin user password
uses these credentials to login and stores them “safe”ly on diskDepending on whether this is your first launch you are presented with either the “onboarding” or “main” window. Telemetry via Hockey is also initialized at this point.
But let’s take a closer look at the storage routine for our most sensitive data:
int k = Array.IndexOf<string>(e.Args, “-auto-login”);if (e.Args.Length > k + 2) {Utils.SafeSave(“STORE_USER”, e.Args[k + 1]);Utils.SafeSave(“STORE_PASS”, e.Args[k + 2]);}
We find this function in TBear.Common, a collection of helper functions. Of course this function has logging built in, but the only thing that’s logged are the names of the keys that are modified. Long story short, the data is encrypted using ProtectedData and saved to the IsolatedStorage KVS. No issues here. Basically all settings are managed within IsolatedStorage.
Account Settings
The fun stuff happens server-side. If you click anything that changes state in your account, you will do so on the TunnelBear website. For example, this is what happens when you click “upgrade” in the UI:
Utils.LaunchBrowser(string.Format(Utils.GetBaseWebsite() + “account#/change?token={0}”, task.Result.Result.ToString()));
The base website is either the production or staging server. But how is the token generated?
new TBearClient().GetDeepLinkToken(Utils.SafeRestore(“STORE_USER”), Utils.SafeRestore(“STORE_PASS”));
That doesn’t seem to have a terribly high amount of entropy considering the average user’s password. Let’s dig deeper.
The DeepLinkToken generator is actually a two-step process, as it consults the API first.
Same as with the front-end, the API’s base URL depends on whether it’s the production version. So it’s [https://(staging).tunnelbear.com/core/](https://%28staging%29.tunnelbear.com/core/)
. But hold on. This API could be blocked. So how does TunnelBear circumvent this?
This is were the API backup IP LUT comes in. It’s filled on startup by updating from AWS. The whole process is a bit convoluted (everything is done with delegated task in TunnelBear), but it boils down to this:
foreach
IP, add [https://ip/core/](https://ip/core/a)
as a backup address to the LUT.The API usually returns four IPs. This is a pretty awesome fallback that I wouldn’t have expected, but the implementation does have to major issues in my opinion:
GetApiBase()
though doesn’t use the IPs, but only the gateway, which is the first entry in the LUT.
But I digress. Back to the login token. Nothing special, which is kind of worrying. It’s a simple POST request to api/core/getLoginToken
. The POST data is
username=turbo
password=nicetry
That’s it. The header also refuses caching and uses the language from the settings. I want to view my token. Now, the fallback actually makes this way easier. I redacted the full API URL above, because the tunnelbear.com API domain has a CloudFlare WAF, so the IP probably shouldn’t be public ;-).
This also interferes with cURL (you just get a CF challenge site if you don’t fake the UA). But using one of the IPs: Success!
$ curl -data “username=minxomat%40gmail.com&password=nicetryagain” -k https://(redacted)/core/getLoginToken
{“details”:”15242acd-****–****-****-7484152ce15c”,”result”:”PASS”}
PASS indicates success, as the server will also send a 200 OK if the request fail. The -k
kills certificate errors (no domain, duh!).
Now, we can see that the token really only does depend on the cleartext password. If you know the e-mail address of someone who uses TunnelBear, you just need to brute-force the password. Of course that is completely infeasible. With the normal URL, you are going to hit CloudFlare limits, with the IPs you can distribute an attack across four points, but your are still going to be discovered as a traffic anomaly pretty quickly.
If you are presented with the website that contains the DeepLinkToken, you’ll still need to log in. But if you are logged in, it’ll take you right where it should.
If your bear is misbehaving, there’s a troubleshooting mode, which can be launched with the command line arg -help
.
An unusual feature of TunnelBear are it’s WiFi management capabilities. If you think about it, this is an essential feature (and currently exclusive to the windows version). It polls the Windows WLAN API to check if the current WiFi network is open and immediately enables the VPN connection even if it was off.
If you have logging enabled, the log will contain the names of all WiFi profiles that were connected and whether they are secure or not. This part of the client doesn’t phone home, so your privacy remains intact.
Another interesting, but essential setting is the vigilant mode. My criticism here is that (probably for UX reasons) it’s disabled by default.
It’s a fact of life, WiFi connections drop. […] Vigilant helps to protect you by blocking any unsecured traffic while TunnelBear is connecting.
So… how?
VigilantFirewall, a class living in TBear.Common, is a thin wrapper around a patchwork of external utils (like everything network-related on Windows).
To block everything, it of course needs to allow exceptions, which are just the program files TunnelBear uses. It uses the Windows firewall API to manage it’s rules. So, no voodoo magic or self-made nonsense.
“Wait. You said privacy! What in the world does it want with a hardware fingerprint?!”, I hear you ask.
Every time an instance of TBearClient is created, it creates a hardware fingerprint. And spoilers again, yes, it is shared with their server. But let’s not get ahead of ourselves. First we need to dissect how this fingerprint is created in the first place. The data used is
But actually, most of this file is dead code. I suspect it has been copy & pastes from something else. The only relevant routine is this:
That’s slightly weird though. It first tries to get the GUID from the 32bit registry. The value returned can be null, in which case it tries the 64bit registry. However, this call could fail, too, potentially setting the fingerprint to null and causing an exception elsewhere.
Anyhow, this function doesn’t call any other method present in this class. OK. Now, who uses this fingerprint?
That. The registration is a larger POST request to the API. The header contains the language and curiously the OpenVPN version. The POST data is:
So it’s pretty clear why the fingerprint is sent on registration. If you combine cURL with a small script and a junkmail API, you could create new TunnelBear accounts within seconds on the same machine, circumventing the paid model altogether. However, all of them would have the same hardware fingerprint and you’d be blocked quickly.
But, this is kind of pointless. Because now you know how to generate bogus fingerprints. The only identifying info then is your IP. But again, use something like TorGateway and simply rotate circuits between each account generation. Infinite free TunnelBear. Stuff for TunnelBear to think about, not a critical bug by any means though.
Other things are pretty routine. Setting proxy info, firing up the VPN etc. Nothing special, and nothing phones home. Certificates are created and installed JIT.
If you just download the free version of TunnelBear, IntelliBear is disabled. It allows you to configure tunneling on a per-URL basis. However, the IntelliBear runtime ships by default and isn’t secured by anything else.
In order to know about what URLs are used, you need to proxy DNS. Now, many of you are probably using a residential internet connection. Which means you are probably also using a DNS proxy without knowing it. I see a lot of people changing their DNS to e.g. Google’s. The truth is: It doesn’t matter. Whatsoever. Even if you think you are using that DNS, and your PC thinks it’s using it. Most ISP employ transparent DNS proxies, that MITM your DNS requests and serve you from a local POP to increase performance. However, these DNS caches often suffer from stupidly high cache times (that you can’t purge) and sometimes even serve ads. If you want to know if you are affected, see this discussion.
Happy to report that TunnelBear has it’s own functions to detect DNS leaks and even IPv6 leaks. Awesome!
Let’s poke the IntelliBear:
C:\Program Files\TunnelBear>intellibear32.exe -helpUsage of intellibear32.exe:-debug=”error”: Debug level, set between error, info or verbose-f=”domains.txt”: Domain list file path-intel=”none”: Is using Intellibear?-log=””: Log path-p=””: Which proto use for lookup domain-r=””: Regex pattern for match domain to use tcp proto-s=”scripts”: Script directory path-server=””: Server ip-t=0: How many seconds to timeout-tap=”utun0": Which Tap adapter to use
The actual wrapper code for IB is missing from the assemblies, though.
After reviewing thousands of lines of code, I can pretty confidentially say that TunnelBear has no privacy issues, doesn’t collect unnecessary telemetry and is a streamlined, fast and intuitive app.
So, if you were looking at the feature set of TunnelBear and you need nothing more, I can recommend it from a privacy standpoint. If you are in desperate need of absolute* privacy, Tor will be the better choice.
All this also means that it’s pretty easy to implement your own TunnelBear client!
You might notice that I didn’t touch on the more advanced features in depth. That’s because V3 is out, where most of these are completely overhauled (hah, get it?). So coming up is a complementary audit of the paid V3 version. Stay tuned.