A look at how LinkedIn exfiltrates extension data from your browser.
How would you feel if you opened a program and the program started to check your file system to see what other programs you had installed? You would probably feel the software was overstepping. This is essentially what LInkedIn does when you visit their website. LinkedIn will scan your local browser files in an attempt to identify a number of different browser extensions you may have installed. The data collected by LinkedIn is then exfiltrated from the browser.
Unraveling the Mystery
This whole adventure started when I was browsing LinkedIn and happened to have my browser console open. While on my LinkedIn profile I noticed a large number of 404 errors and as a developer it piqued my interest.
What really piqued my curiosity was the fact all of these failed web requests were for
chrome-extension:// resources. After inspecting a few of the extension IDs in the resources I began to suspect LinkedIn was attempting to determine if I had certain extensions installed by executing local web requests to the browser itself.
I spent some time toying around with my browser and LinkedIn's assets. Note, reverse engineering LinkedIn's source code is apparently against their terms of service. If I was LinkedIn I would probably not want people figuring out how I spy on them either.
After poking around and doing some investigation I found an interesting object in one of LinkedIn's local storage values.
LinkedIn's Extension File
One of LinkedIn's local storage keys is
C_C_M. The value itself is a base64 encoded string (which isn't too abnormal). However, if you decode the string you will see a large JSON blob that seems to be encoded with unicode code points (not human readable).
I am not too sure how or why they encoded it that way, but it seems to me like it was in an attempt to obfuscate the data. The encoding is easy enough to reverse, simply parse the JSON. You can do it yourself with the following snippet:
How does LinkedIn search for extensions?
After examing the JSON file it became pretty clear what was going on. LinkedIn is using two different methods to determine if you have an extension installed.
- Content changes.
- Web accessible resources.
The first method is the simplest. LinkedIn simply looks for certain content on the page they know doesn't belong there. For instance, if they find a div with the ID
email-hunter, they know you have the Email Hunter extension installed (and now your account is probably restricted, or at least on a blacklist).
The second method is a lot more interesting to me. When building an extension you can specify web accessible resources. These resources are typically used via a content script to build a custom interface. However, there's a bit of a gotcha. If the content script can make a request for the web accessible resource, so can the underlying website. LinkedIn abuses this fact and sprays web requests to your local browser in attempt to find extensions.
I built a simple extension to automatically parse the extension file and display extensions LinkedIn is looking for. Check it out here.
What can you do as a developer to prevent detection?
So, as a developer of an extension what can you do about this? I recommend not using web accessible resources. Out of all extensions LinkedIn finds, a majority of them are due to web accessible resources.
I would recommend not modifying or injecting user interface features into the underlying page. Alternatively, I would use a browser action and communicate with the content page through messaging passing. That is how my extensions (Nymeria and The Reach) work.
Interested in an extension that can automatically parse LinkedIn's extension file and show you which extensions LinkedIn is searching for? I have created a tool for it! Check it out here.