Security Issue (XSS) in Translators mechanism

278 views
Skip to first unread message

Barak Sternberg

unread,
Jun 7, 2021, 3:50:00 AM6/7/21
to zotero-dev

Hi, I have tried my best to give the best explanation of this bug to make sure our beloved Zotero (for real :)) stays safe as it can be! Hope to work with you on a quick fix for that soon enough! Thanks a lot! 

Description is as follows:

Zotero version: 5.0.86 (May 28, 2021)

Chrome version: 91.0.4472.77

OS version: Windows 10 Home 19042.985

When Zotero update and get new translators code – it doesn’t validate authenticity of the files.

Hence, attacker can exploit this, using a limited permissions chrome-app to exploit and run JS code inside Zotero content-scripts with all of Zotero’s permissions. This can be also exploited from low-priv app running on PC.

Relevant Vulnerable lines:

Actually the call for getTranslatorsCode, which always try first to reach the local Zotero Server running on PC – will download from localhost at TCP port 23119 all of the translators JS files.

https://github.com/zotero/zotero-connectors/blob/be6b43a640e753d5c3f9e5dbcdbf59239935e0d9/src/common/repo.js#L64

https://github.com/zotero/zotero/blob/52932b6eb03f72b5fb5591ba52d8e0f4c2ef825f/chrome/content/zotero/xpcom/connector/server_connector.js#L1497

PoC & Reproduce Instructions:

This PoC shows an exploit where one low-privilege chrome-app exploits Zotero to gain more privileges. Attacker can use this as a way to gain limit-less access to any site!!!

Poc Video: https://youtu.be/3Rz_M6SJWX8

Steps to reproduce:

1.       Download “Mappy” chrome-app from here (just checkout all directory):

https://github.com/barakolo/ExtensionExploiter/

2.       Install this github as a chrome-app, its name will be “Mappy”.

a.       goto chrome://extensions

b.       switch to developer mode.

c.       Then click – “load unpacked” – click over the downloaded “Mappy” directory.

d.       Observe – that this “Mappy” chrome-app – have no permissions to any site!!!

3.       Open your own server listener in: http://localhost:8080/ (This will get victim’s data – this can be any site controlled by attacker and not just localhost).

4.       Restart Zotero – and open new tab – in google.com for example.

5.       You will now see that the backend server (stage 3) – gets all cookies / running js in google context.

What can be a fix for that?

The easiest way would be to disable download of translators code over Zotero localhost RPC, and user only verified Zotero HTTPS servers for downloading new JS files and executables.

But if one wants to add some more features/extensibility for users – then adding validation when downloading any JS file – and also verify it has a valid translator hash – using previously given set of valid hashes downloaded from trustworthy source, such as Zotero github or something like that? Then, when downloading new JS files – validate its hash is one of these hashes previously downloaded. Also, update the hashes from time to time to allow users to update translators code and add more of it over again.

This translator code can be a security hazard, and can really take over Zotero from other extensions/apps in other contexts as well, hence elevating privileges and can be a useful stuff in other type of attacks as well.

Dan Stillman

unread,
Jun 7, 2021, 3:54:30 AM6/7/21
to zoter...@googlegroups.com
Hi Barak,

Thanks very much for the detailed report and PoC.

We're aware of this behavior, which has been in place for a decade, and we don't consider it a meaningful vulnerability. Our longstanding view has been that there's very little point in trying to protect against a rogue local app, which can likely do all manner of things by virtue of running on your machine. If an app can bind to a local port, there's a good chance it also has filesystem access, in which case it would do well to encrypt all your files and hold them for ransom rather than hope you have the Zotero Connector installed and try to serve bad Zotero translators.

(I think the Chrome App thing is mostly a distraction. It's a pretty idiosyncratic permissions model to allow creation of a local TCP server by default while preventing filesystem access, and in any case Chrome Apps are discontinued for most people this month after a year's delay, and were rarely used before then. The important question here is regular local apps.)

Our position on local threats has been Mozilla's historical position, and as far as I'm aware, filesystem access still gets you access to the Firefox cookie database as well as the ability to set a proxy in prefs.js and install a custom CA. There's no point worrying about a rogue localhost server if there are already multiple ways for a local app to get your website data more directly.

We recognize that Google takes a bit of a different posture here, encrypting the Chrome cookie database and relying solely on system proxy settings. I don't know how secure it really is from arbitrary filesystem access, but there's at least an argument to be made that, as a Chrome extension, the Zotero Connector should respect Google's defense-in-depth approach to webpage access.

In any case, Manifest V3 will render this moot, as eval'ing code in content scripts won't be possible, and translators will run in a sandbox without access to the DOM. We're currently working on those changes and should be rolling them out later this year.

- Dan

P.S. We didn't verify this, but we believe your Step 3 isn't quite accurate. Current versions of Chrome and Firefox should prevent content scripts from making cross-origin requests, so it shouldn't be able to be "any site" -- localhost just gets special treatment. But doesn't make a difference, because the data could just be passed to the rogue local app, which could exfiltrate the data itself.

Barak Sternberg

unread,
Jun 8, 2021, 7:46:50 PM6/8/21
to zotero-dev
Hi Dan and thanks and appreciation for your fast comments!
Starting with the last, If I have understood you correctly,  this is not true, step 3 can be easily done with CORS headers being set-up in the remote attacker server (this is why we doing the requests, just to post secret data to the attacker for example), as content-scripts are limited the same way as the website they are running in are limited to (https://www.chromium.org/Home/chromium-security/extension-content-script-fetches), also, even if blocked, content-scripts can still generate GET requests with simple payload data they observed inside the current targeted site. so it is wrong 

Regarding the local-apps attack case, nowadays local apps use the permissions model and many are not accessible to anything but a couple of sockets and their own directory as low-privileged specific users. Access to the whole filesystem and especially to chrome internal configurations and such is not a trivial thing for a normal app even after running (especially in the sandboxed environment) without getting suspected as a malicious attack. So, this can truly help attackers elevate privileges and even inject their own version of malicious JS code inside of Zotero.

I think that the main issue here is the insecure default communication between Zotero connector agent and the browser which can be exploited in some ways, this contains a bunch of API's, JS code and users' private data being transferred without any authorizations routines and such. What do you think?

Regarding Manifest v3, this is not mandatory for extensions, just recommended (https://developer.chrome.com/docs/extensions/mv2/) - Do you plan to make the shift soon? as I think it might take a while - current extensions won't probably reach there so soon (as in many and also in Zotero - using eval's and other JS dynamic executions is common, so it's not that simple to make the switch) and that means the things in Zotero will stay there for a while. 

Regarding chrome apps - Although this will be disabled in the Webstore soon, I think this is still deployable on enterprise-level and such. Yes, it limits the attack surface but it's still there.

Thanks again for the response,
Barak.





 




Dan Stillman

unread,
Jun 8, 2021, 8:57:37 PM6/8/21
to zoter...@googlegroups.com
On 6/8/21 7:40 PM, Barak Sternberg wrote:
> Regarding the local-apps attack case, nowadays local apps use the
> permissions model and many are not accessible to anything but a couple
> of sockets and their own directory as low-privileged specific users.
> Access to the whole filesystem and especially to chrome internal
> configurations and such is not a trivial thing for a normal app even
> after running (especially in the sandboxed environment) without
> getting suspected as a malicious attack. So, this can truly help
> attackers elevate privileges and even inject their own version of
> malicious JS code inside of Zotero.

I'm not sure which environments or permissions models you're referring
to, but this is not my understanding of the current state of desktop
computing. Obviously there's some movement in that direction in some
environments (e.g., Flatpaks on Linux, apps from the Mac App Store), but
I would say most "normal" apps absolutely have access to the filesystem,
and users are still entirely used to running such software.

And if an app were sandboxed, depending on the environment it would
likely need to explicitly request permission to listen on an arbitrary
socket, which would be a suspicious thing to request.

> I think that the main issue here is the insecure default communication
> between Zotero connector agent and the browser which can be exploited
> in some ways, this contains a bunch of API's, JS code and users'
> private data being transferred without any authorizations routines and
> such. What do you think?

We mostly disagree. We think that apps should generally be able to
communicate via localhost without worrying about rogue processes on the
machine intercepting or faking the traffic, because those apps can
likely already do other, worse things — like grab the Firefox cookie
database, or the Zotero database — and localhost HTTP communication
provides real value.

Again, I think there's a fair argument that a Chrome extension should
respect Chrome's defense-in-depth measures and not eval() code received
from a local port in any sort of privileged environment, and after MV3
the Connector will stop doing that. But I don't think trusting localhost
is an unreasonable thing to do in general.

> Regarding Manifest v3, this is not mandatory for extensions, just
> recommended (https://developer.chrome.com/docs/extensions/mv2/) - Do
> you plan to make the shift soon?

As I say, we're currently working on changes for MV3 and plan to release
those changes later this year. (Google clearly intends for MV3 to be
mandatory, even if there's not yet a timeline.) But this behavior is a
decade old and would be a bizarre choice of attack for an app that
convinced you to run it on your computer, so we don't consider this to
be a significant or urgent problem.

- Dan

Dan Stillman

unread,
Jun 8, 2021, 9:09:53 PM6/8/21
to zoter...@googlegroups.com
On 6/8/21 8:57 PM, Dan Stillman wrote:
> On 6/8/21 7:40 PM, Barak Sternberg wrote:
>
>> I think that the main issue here is the insecure default
>> communication between Zotero connector agent and the browser which
>> can be exploited in some ways, this contains a bunch of API's, JS
>> code and users' private data being transferred without any
>> authorizations routines and such. What do you think?
>
> We mostly disagree. We think that apps should generally be able to
> communicate via localhost without worrying about rogue processes on
> the machine intercepting or faking the traffic, because those apps can
> likely already do other, worse things — like grab the Firefox cookie
> database, or the Zotero database — and localhost HTTP communication
> provides real value.

On this, I'll add that, if we extended the client's HTTP server with
additional functionality, such as replicating some of the web's API
functionality from the local Zotero database (which is something we've
discussed), we'd at least reevaluate these assumptions. But I'd remain
somewhat skeptical of any mitigations. You could require a connecting
local tool to perform a pairing process and provide a token, or require
it to pass a user-specified password, but I would still expect most
local apps to be able to simply read those values from the Zotero
database or wherever they're stored, at which point they could just grab
the Zotero database instead.

Barak Sternberg

unread,
Jul 9, 2021, 4:11:53 PM7/9/21
to zotero-dev
Although I understand what you are saying, I think this is not true in practice and token pair is still a great mitigation,
I will explain a new attack scenario with DNS-rebinding:
1.  As an attacker, you could target Zotero standalone to query users' data and post new items and stuff (potentially maybe also new JS).
2. An attacker can easily use DNS-rebinding to bind its own site to 127.0.0.1 on zotero port - 21139,
So, first time the user enters: attacker.com:21139 (returns real dns address at attacker site with its JS, the dns response will have low timeout so any other 
query to this server will require dns resolving again).
Second time - the attacker will then call attacker.com:21139 (which now resolves to 127.0.0.1:21139) with its own parameters from its JS context.
3. This will make calls inside zotero standalone client easily because no Verification/Auth was required and no token initialized.

Therefore, some token/auth at first time is relevant, if you could keep one connection at a time with websockets this can be cool also (although might not be wide enough protection and needed to be inspected throughly). 

Dan Stillman

unread,
Jul 9, 2021, 4:16:46 PM7/9/21
to zoter...@googlegroups.com
On 7/9/21 8:01 AM, Barak Sternberg wrote:
> Although I understand what you are saying, I think this is not true in
> practice and token pair is still a great mitigation,
> I will explain a new attack scenario with DNS-rebinding:
> 1.  As an attacker, you could target Zotero standalone to query users'
> data and post new items and stuff (potentially maybe also new JS).
> 2. An attacker can easily use DNS-rebinding to bind its own site to
> 127.0.0.1 on zotero port - 21139,
> So, first time the user enters: attacker.com:21139 (returns real dns
> address at attacker site with its JS, the dns response will have low
> timeout so any other
> query to this server will require dns resolving again).
> Second time - the attacker will then call attacker.com:21139 (which
> now resolves to 127.0.0.1:21139) with its own parameters from its JS
> context.
> 3. This will make calls inside zotero standalone client easily because
> no Verification/Auth was required and no token initialized.

Nope — we block DNS rebinding attacks with a Host header check.
Reply all
Reply to author
Forward
0 new messages