- cross-posted to:
- firefox@lemmy.world
- hackernews@lemmy.smeargle.fans
- hackernews@derp.foo
- cross-posted to:
- firefox@lemmy.world
- hackernews@lemmy.smeargle.fans
- hackernews@derp.foo
Date: Tue, 17 Oct 2023 03:17:36 +0300 From: turistu To: oss-security@…ts.openwall.com Subject: with firefox on X11, any page can pastejack you anytime
Note to the moderator: I have already submitted this to the firefox people three weeks ago, and according to them, this is not a real security issue, or at least not worse than those pesky scripts which you cannot kill without killing firefox itself; if you think the same, just ignore this without replying.
I would however appreciate if you let this through and so give it some visibility so that the other 2 or 3 people who may be affected by this could learn about it.
Thank you very much.
====
In firefox running on X11, any script from any page can freely write to the primary selection, and that can be easily exploited to run arbitrary code on the user’s machine.
No user interaction is necessary – any page able to run javascript can do it,
including e.g. a page from a background tab of a minimized window, an iframe
inside such a window, an error page, a sandboxed iframe, a page that has
reloaded itself via meta http-equiv=refresh
, etc.
This applies to all the versions of mozilla/firefox and their derivatives (seamonkey, etc) that I was able to test, including the latest nightly.
Example
The simplest example, which works in the default configurations of systems like OpenBSD or Alpine Linux (= any Unix/Linux system where Wayland is not the default and the default shell does not implement bracketed-paste), would go like this:
Load the following snippet in firefox:
<pre></pre>
intentionally left blank
Then pretend to forget about it, and go about your work. Sooner or later,
when trying to paste something in the terminal with shift-Insert or middle
click, you will end up running the command writeXPrimary()
has injected
just between your copy and paste.
live example of that snippet: https://turistu.github.io/firefox/pastejack.html
Short technical explanation
Browsers like firefox have the concepts of “secure context” (e.g. https://
)
and “transient user activation”; the javascript from the page gets some
temporary powers as soon as you have interacted even so little with the
page, like clicked, touched, etc.
For instance, writing with Clipboard.writeText()
to the windows-style
Ctrl-C Ctrl-V clipboard selection is only possible from secure contexts
and only in the short while after the user has clicked a button, etc on the page.
As this bug demonstrates, those prerequisites are not needed for writing to the
primary selection, which on X11 is much more used and much more valuable.
Workaround
Without patching firefox, the only workaround I can think about is
disabling the Clipboard.selectAllChildren()
function from an addon’s
content script, e.g. like this:
let block = function(){ throw Error('blocked') };
exportFunction(block, Selection.prototype, { defineAs: 'selectAllChildren' });
Complete extension here at https://github.com/turistu/odds-n-ends/raw/main/firefox/no-sel.xpi.
I tried to submit it to addons.mozilla.org but they didn’t accept it. If
you’re running firefox-esr, the development edition or nightly, you can just
set xpinstall.signatures.required
to true in about:config
and install
it with firefox no-sel.xpi
.
Firefox Patch
diff -r 9b362770f30b layout/generic/nsFrameSelection.cpp
--- a/layout/generic/nsFrameSelection.cpp Fri Oct 06 12:03:17 2023 +0000
+++ b/layout/generic/nsFrameSelection.cpp Sun Oct 08 11:04:41 2023 +0300
@@ -3345,6 +3345,10 @@
return; // Don't care if we are still dragging.
}
+ if (aReason & nsISelectionListener::JS_REASON) {
+ return;
+ }
+
if (!aDocument || aSelection.IsCollapsed()) {
#ifdef DEBUG_CLIPBOARD
fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
The idea of this patch was to always prevent javascript from indirectly
messing with the primary selection via the Selection API. However, it turned
out that the JS_REASON
flag was not reliable; if javascript calls some
function like addRange()
or selectAllChildren()
while the user has started
dragging but hasn’t released the mouse button yet, that code will be called
without that flag but with the text set by javascript, not the text
selected by the user. However, I think that this patch is still enough
to fill the glaring hole opened by selectAllChildren()
.
About the example and bracketed-paste
The bracketed paste feature of bash/readline and zsh means that you cannot just append a CR or LF to the payload and be done, it’s the user who has to press ENTER for it to run.
However, workarounds exist. For instance, some terminals like mlterm
don’t filter out the pasted data, and you can terminate the pasting
mode early by inserting a \e[201~
in the payload.
For bash, you can take advantage of some quirks in the readline library to turn off the highlighting and make the payload invisible to the user. E.g.:
let payload = 'touch ~/LOL-' + Date.now() / 1000;
writeXPrimary('\n' + payload + '\n'.repeat(100) + ' '.repeat(30)
+ '\n'.repeat(100))
which will confuse the user with the same screen as when some stray background job had written something to the terminal:
user@...t:~$ : previous unrelated command
user@...t:~$ <-- paste here
# <-- cursor here, most users will just hit Enter to get a new prompt
live example of that snippet: https://turistu.github.io/firefox/bash-pastejack.html
Just to be clear, I don’t think that either mlterm, bash, nor the shells that don’t do have that bracketed-paste feature are at fault here in any way (and I personally always turn off that misfeature as it badly interferes with my workflow): It’s firefox which should get all the blame for letting random javascript evade its pretended “sandbox” in this way.
About Wayland
For firefox running in Wayland, writeXPrimary()
will only succeed
when the firefox window (the main window, not necessarily the tab the code
runs in) has the focus. Otherwise the selection will be cleared. At first I
assumed that this is something specific to the Wayland protocol, but that
turned out to be utterly false; it’s just some quirk, bug or “feature”
specific to either firefox itself or GTK.
But I think that’s still bad enough, even if the page should take care to only set the selection when the main window has gained focus.
And of course, all this doesn’t affect the situation where you’re copying and pasting in another firefox tab with a different context, origin, etc; and all the other situations where you don’t appreciate having random javascript you don’t even know about messing with your copy & paste.
===
This is a slightly edited version of https://github.com/turistu/odds-n-ends/blob/main/firefox/pastejack.md.
I will correct any errors or omissions and also add more info there.
The included example script re-runs the selection every 500ms so it would instantly overwrite what the user has selected. In theory you could even lower this timer.
That doesn’t do anything to facilitate the attack, on the contrary. In order for the primary selection to work, the selected text must stay marked as selection until you paste, and everybody who uses primary selection has been conditioned to watch for that selection. If it were to suddenly dissapear you would assume you clicked somewhere by mistake, and attempt to select again, and again. You wouldn’t attempt to paste because you’ve been taught that the selection must hold in order to be able to paste. My next reaction would be to use the regular clipboard instead.
If anything, the script should attempt a longer retry, not a shorter one. A few seconds might be just right to catch someone after they’ve selected and are moving to the paste window, but before they’ve pasted the good text. But since the script is looping blindly and has no way to know when to inject, it would have to be a purely coincidental good timing.
I can’t imagine that this will manage to succeed very often.
It’s a simple POC. To address your points you could easily add an event listener for the window blur event so whenever the window loses focuses. You could also use javascript to manually highlight the user selected text when the window regains focus. You can make it as complex as you wanted.
The point is that the core of the issue, that you can override the users select buffer which could be used to maliciously insert commands, exists.
The Firefox window is not necessarily involved, the user could be copy-pasting between two other windows.
Look, I agree it’s annoying and it should be fixed. God knows Firefox is full of dumb little quirks that should have been fixed 10 years ago. But it’s not the gruesome attack vector it’s made out to be. It is a vector but not a great one, too many planets need to align for anything to happen.
I 100% agree that it effects an extremely small percentage of the population, but it’s also not hard to imagine a scenario in which this can have real consequences.
Let’s imagine I have a popular website that documents Linux tips and tricks (think: which command can I run to see drive storage used again?). In there I have a short command people can copy and paste to run (maybe
df -h
). The user copies this command and switches window to their terminal, at which point the blur event listener fires and I override the innocuous command with a malicious command. The user pastes it into their terminal without any indication that the primary selection content is now different.Yes, this is due to both insecure X and shell settings that doesn’t effect everyone (Wayland and sane shell). It’s as much or even more the fault of the insecure programs, but Firefox is a part of that. Even in this situation it would be much more likely that the user is effected compared to the “general population”. It’s more of a targeted attack than a broad insecurity, but it’s not a “one in a million” chance.