On X11, any page can freely write to the primary selection
Categories
(Core :: DOM: Selection, defect)
Tracking
()
People
(Reporter: u734064, Assigned: masayuki)
References
()
Details
(Keywords: reporter-external, sec-moderate, X11, Whiteboard: [adv-main120+][adv-esr115.5+][reporter-external] [client-bounty-form] [verif?])
Attachments
(6 files, 1 obsolete file)
427 bytes,
text/html
|
Details | |
318 bytes,
text/html
|
Details | |
4.18 KB,
text/plain
|
Details | |
48 bytes,
text/x-phabricator-request
|
diannaS
:
approval-mozilla-beta+
dmeehan
:
approval-mozilla-release-
diannaS
:
approval-mozilla-esr115+
|
Details | Review |
900 bytes,
text/html
|
Details | |
315 bytes,
text/plain
|
Details |
In Firefox running on X11, any script from any page can write to the
primary selection -- which on X11 is the main and most used copy-paste
mechanism.
No "secure context", "transient activation", extra permissions or any other
prerequisites (as required by the much less valuable Clipboard.writeText())
are needed.
This basically means that any visited page can take over the victim's system
the first time she tries to paste something in the terminal.
With prior knowledge of the user's workflow and preferences, devising a
succesful attack is a matter of minutes. Getting something that works
reliably for most Linux/Users users is more complicated but still orders
of magnitude easier than exploiting some memory corruption or similar bug.
=== Example ===
Load the following html snippet inside a Firefox running on top of Xorg
(Xorg, not Wayland, see below why). The protocol (http:, file:, etc) or
whether this runs inside an iframe does not matter.
Then switch to a gnome-terminal or xterm window, and press the middle
button of the mouse or shift-Insert to paste the selection.
Every 500ms you'll get a new text made up of the current time and the
page's url.
------------------x-------------------
<!doctype html>
<title>write to the primary selection</title>
intentionally left blank
<script>
function setXPrimary(text){
let d = document.body.appendChild(document.createElement('pre'));
d.style.fontSize = 0;
setTimeout(function(){
d.textContent = text;
getSelection().selectAllChildren(d);
d.remove();
}, 0);
}
setInterval(function(){
setXPrimary(Date.now() / 1000 + ' ' + document.location)
}, 500);
</script>
------------------x-------------------
=== NON-mitigating factors ===
-
Wayland only allows the window having the keyboard focus to write to
the primary selection. This complicates things a bit, but not for much; I
think it's possible to detect via timing DOM calls, etc. whether the main
firefox window has acquired the focus and only then write the selection,
even if it's not the tab or iframe the script runs in that has the focus. -
The "bracketed paste" feature of the terminal and shell (the default
in recent bash) means that you cannot just use "setXPrimary('rm -fr ~\n')"
and be done: it's the user who has to press the Enter for it to run.
However, even naive workarounds like
setXPrimary("\n" + "echo reboot" + "\n".repeat(100) +
" ".repeat(30) + "\n".repeat(100))
which will confuse the user with a screen like
dummy@ohzd:~$ echo first
first
dummy@ohzd:~$ true
dummy@ohzd:~$
# <-- cursor here, most users will hit Enter to get a new prompt
will do for it most of the time.
looks like the example html snippet was mangled in the description; here is the same example, as an attachment
Comment 2•1 year ago
|
||
I'll start this off in DOM Selection, but maybe there's a Widget GTK component to this.
Updated•1 year ago
|
Comment 3•1 year ago
|
||
We should probably only set the primary clipboard if there's a user interaction or so, perhaps?
Comment 4•1 year ago
|
||
(In reply to Emilio Cobos Álvarez (:emilio) from comment #3)
We should probably only set the primary clipboard if there's a user interaction or so, perhaps?
Yes, that seems the most plausible option. It'd also be interesting to know what other browsers do here...
Comment 5•1 year ago
|
||
watch -n 1 xsel
may be useful.
Different behavior was reported for HTMLInputElement.setSelectionRange()
. I wonder why Selection.selectAllChildren()
would be different.
With MOZ_DISABLE_CONTENT_SANDBOX=1 mozregression --launch 2021-01-01
, I see similar behavior but the selection is often being set to empty, for reasons I do not know.
Chromium at least tests for user-input.
Odd that sites have wanted this behavior for a long time but haven't discovered it.
(In reply to :Gijs (he/him) from comment #4)
(In reply to Emilio Cobos Álvarez (:emilio) from comment #3)
We should probably only set the primary clipboard if there's a user interaction or so, perhaps?
Yes, that seems the most plausible option. It'd also be interesting to know what other browsers do here...
I would very much prefer to have it as an option of Clipboard.writeText()
or a separate method that I could catch and block from an extension's content script with exportFunction()
. That would also be more straightforward to use from apps like editors and such.
Having the primary selection set as a hidden side effect of using the Selection API is still icky, even if gated by user interaction checks or similar.
(In reply to Karl Tomlinson (:karlt) from comment #5)
With
MOZ_DISABLE_CONTENT_SANDBOX=1 mozregression --launch 2021-01-01
, I see similar behavior but the selection is often being set to empty, for reasons I do not know.
That's probably because of some race triggered by my badly over-engineered example (sorry).
Here's a simplified example which should work more reliably in all versions from at least firefox-42.0 through the lates nightly (120.0a1).
<!DOCTYPE HTML>
<html lang=en>
<title>write to the primary selection (#2 take)</title>
<pre id=pre style=font-size:0></pre>
intentionally left blank
<script>
setInterval(function(){
pre.textContent = Date.now() / 1000 + ' ' + document.location + '\n';
getSelection().selectAllChildren(pre);
}, 500)
</script>
Assignee | ||
Comment 9•1 year ago
|
||
Selection.selectAllChildren
is mapped to Selection::SelectAllChildrenJS
. The method sets mCalledByJS
to true
. Then, AutoCopyListener::OnSelectionChange
is called with nsISelectionListener::JS_REASON | nsISelectionListener::SELECTALL_REASON
. Finally, only nsISelectionListener::SELECTALL_REASON
is checked to consider whether the primary selection is updated.
Assignee | ||
Comment 10•1 year ago
•
|
||
I'll post a patch for just avoiding that only Selection.selectAllChildren
can update the clipboard. I think that we should discuss better handling in bug 1567160.
Comment 11•1 year ago
|
||
(In reply to turistu from comment #6)
Having the primary selection set as a hidden side effect of using the Selection API is still icky, even if gated by user interaction checks or similar.
What do you think should happen instead?
If a website has a "select all" button (or context menu entry...) next to a rich text editor (contenteditable), that uses the selection API to select text, wouldn't the expected behaviour on X11 and similar be that this makes it to the primary selection?
Reporter | ||
Comment 12•1 year ago
|
||
(In reply to :Gijs (he/him) from comment #11)
(In reply to turistu from comment #6)
Having the primary selection set as a hidden side effect of using the Selection API is still icky, even if gated by user interaction checks or similar.
What do you think should happen instead?
Highlight the text without putting it into the primary selection.
AFAICS, there are 4 ways out of this.
-
Only allow the Selection API functions when called as a result of user action (the so called "transient activation") and only from secure contexts. Totally unacceptable as it would break a lot of sites which are using the Selection API just for highlighting search results and other text (instead of inserting
<span>
elements or other ugly kludge). -
Allow the Selection API to be used at any time, but only set the primary selection when some conditions like "transient activation", https, etc are met. A script could call
addRange()
andselectAllChildren()
in the hope that that would also copy the text to the clipboard, but would have no way to know if that actually happened at all (unlike withClipboard.writeText()
, which returns a promise). Writing non-visible or non-highlighted text to the selection would require sordid hacks like transparent elements. Malware would still be able to abuse it to write to selection something else than what the user has selected. Extensions would not be able to block them without breaking things in the same way 1. would. -
Allow the Selection API to be used at any time (and continue to work as a precursor to windows-style Ctrl-C and
execCommand('copy')
as it does now) but without affecting the primary selection in any way. Implement copying to the primary selection either as an option ofClipboard.writeText()
, a separate Clipboard API function (subject to the same or more stringent conditions thanwriteText()
), or makewriteText()
set bothX_PRIMARY
andX_CLIPBOARD
by default. For most Linux/BSD users, the "Copy" buttons will instantly start to work on most sites (or could be easily made to) in the same way "Copy outerHTML" does in the firefox's own web dev pane. Extensions would be able to selectively block that API from a content script withexportFunction()
. -
Keep things as they are and just document them (so that people know what to expect and make their own assessment).
If a website has a "select all" button (or context menu entry...) next to a rich text editor (contenteditable), that uses the selection API to select text, wouldn't the expected behaviour on X11 and similar be that this makes it to the primary selection?
Yes it would be nice it that just worked as expected, but letting that work via 2. is too dangerous (yet not at all reliable!), while 3. would easily allow to achieve that in a simple and obvious way.
Comment 13•1 year ago
|
||
The security considerations should be the same as for the regular clipboard API write: only from a secure context, only with a user activation.
Reporter | ||
Comment 14•1 year ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #13)
The security considerations should be the same as for the regular clipboard API write: only from a secure context, only with a user activation.
I find the sec-moderate label COMPLETELY INNACURATE -- for a bug which allows any visited site to simply take over the user's machine without them even having to interact in any way with the site.
Also the linux-only tag: this actually works on *BSD and Solaris too.
And also, because the way it's used and the way it works, writing to the primary selection on X11/Unix is much more dangerous than writing to the clipboard on Windows or Mac.
Reporter | ||
Comment 15•1 year ago
|
||
(In reply to Karl Tomlinson (:karlt) from comment #5)
watch -n 1 xsel
may be useful.
FWIW, the Xfixes X11 extension allow you to monitor selection changes in real time, no need to poll for it every second or so.
Reporter | ||
Comment 16•1 year ago
|
||
(In reply to turistu from comment #14)
I find the sec-moderate label COMPLETELY INNACURATE
Sorry for the misspelling, so that should've read INACCURATE
the way it works, writing to the primary selection on X11/Unix
Comment 17•1 year ago
|
||
Please do not adjust the sec severity flags yourself.
Needinfo'ing Dan for consideration of comment #14 re: sec-severity.
Reporter | ||
Comment 18•1 year ago
|
||
(In reply to :Gijs (he/him) from comment #17)
Please do not adjust the sec severity flags yourself.
And why not, if the interface allows it?
I've just corrected a bogus flag. It's not like I can take over your "processes" or force you to do anything by just pushing buttons here (or could I?).
The descriptions of the severity flags say:
sec-moderate
Vulnerabilities which can provide an attacker additional information or positioning that could be used in combination with other vulnerabilities.
sec-high
Obtain confidential data from other sites the user is visiting or the local machine, or inject data or code into those sites, requiring no more than normal browsing actions. Exploitable web vulnerabilities that can lead to the targeted compromise of a small number of users.
sec-critical
Exploitable vulnerabilities which can lead to the widespread compromise of many users.
This is a bug where a random page from a background tab can run arbitrary code on the user's computer (possibly with root privileges, if the user is into sudo
or doas
), without the user having to interact with the page in any way. There are systems (Alpine Linux, OpenBSD, etc) where even the cartoon version of the exploit (writeXPrimary('rm -fr ~\r')
) works 100% in their default configuration.
Bypassing hurdles like the bracketed-paste feature of readline/bash or zsh requires more skill than that, but it's still easy. If you think that that is a big hurdle, or that the lack of such of such feature qualifies itself as a vulnerability -- i.e. each program (but firefox itself, of course) should treat any user input as potentially hostile, remote-controlled data, then please state it clearly to have it on record here.
Needinfo'ing Dan for consideration of comment #14 re: sec-severity.
No info is forthcoming. There are 10 days already with 0 technical engagement (besides the odd vacuous statement) from your part. It looks more and more like sharing this info with you was a horrible terrible mistake from my part.
Reporter | ||
Comment 19•11 months ago
|
||
(In reply to turistu from comment #12) > AFAICS, there are 4 ways out of this. > > 3. Allow the Selection API to be used at any time (and continue to work as a precursor to windows-style Ctrl-C and `execCommand('copy')` as it does now) but **without affecting the primary selection in any way**. Implement copying to the primary selection either as an option of `Clipboard.writeText()`, a separate Clipboard API function (subject to the same or more stringent conditions than `writeText()`), or make `writeText()` set both `X_PRIMARY` and `X_CLIPBOARD` by default. For most Linux/BSD users, the "Copy" buttons will instantly start to work on most sites (or could be easily made to) in the same way "Copy outerHTML" does in the firefox's own web dev pane. Extensions would be able to *selectively* block that API from a content script with `exportFunction()`. Just for the record, here is a patch implementing this. The first part remove javascript's ability to indirectly mess with the system's clipboard. AFAIK this should only affect `Selection.selectAllChildren()`; other functions of the Selection API ('.addRange()`, etc) or `HTMLInputElement.setSelectionRange()` were **already broken**, so this does not really change anything, beside fixing the vulnerability. The only code that this would break is probably my own little extension. The second parts adds an option to use the primary selection to `Clipboard.write()` and `.writeText()`. Sample usage is: ``` navigator.clipboard.writeText(some_text, {useX11Primary: true}) ``` Should you take this seriously, I can do the same for `.read()`, and also write some tests and such. ``` ```
Comment 20•11 months ago
|
||
Needinfo'ing Dan for consideration of comment #14 re: sec-severity
I obviously don't understand how the X11 Primary Clipboard is used well enough to rate this.
Comment hidden (obsolete) |
Comment 22•11 months ago
|
||
While it is true that if the attacker pulls it off, they can do basically anything, it does require some specific steps from the user, which reduces the danger a bit. The user has to go to a console and hit paste (and possibly enter), while the attacker payload is still in the buffer.
If the user goes to hit paste in the console, they'd presumably be hitting copy themselves at some point for the text they want, which will replace the attacker payload. If we assume they aren't selecting text themselves from the attacker page (because if they were, the ability to bypass the user interaction requirement would matter less), then that means the attacker page is more likely to be in the background, and we throttle setInterval and other ways to run script when a page is in the background (to reduce energy usage), so it would be harder for the page to re-overwrite the buffer during that time.
Reporter | ||
Comment 23•11 months ago
|
||
(In reply to Andrew McCreight [:mccr8] from comment #22)
While it is true that if the attacker pulls it off, they can do basically anything, it does require some specific steps from the user,
Yes, the most specific step of which is using firefox at all
which reduces the danger a bit. The user has to go to a console and hit paste (and possibly enter), while the attacker payload is still in the buffer.
There's of course no clipboard "buffer" whatsoever on X11. Neither for the primary nor for the clipboard selection.
If the user goes to hit paste in the console, they'd presumably be hitting copy themselves at some point for the text they want, which will replace the attacker payload
What are you talking about "going to paste in the console" and "hitting copy"? Have you even read my bug report? Are you even using an X11 system at all?
The user is happily working in their terminal or editor, and then --without having to do anything out of the ordinary-- she gets powned by a page from a background tab from a minimized firefox window which has hijacked the selection just in between a copy and a paste. That's it.
then that means the attacker page is more likely to be in the background, and we throttle setInterval and other ways to run script when a page is in the background (to reduce energy usage)
Throttling does not matter at all -- in fact, trying to hijack the selection faster than 500ms or so would be counterproductive, because the user will immediately notice that she's not able to select anything. Unlike firefox, most X11 program will de-highlight the text upon losing the primary selection's ownership. And 500ms-3s is exactly the sweet spot between selecting and pasting for most users.
But all that's about my silly example, which was only meant as an illustration. But there is at least one terminal emulator with at least a couple thousand users where even karlt's example (watch -n1 xsel
) is more than enough to run arbitrary code on the user's machine.
so it would be harder for the page to re-overwrite the buffer during that time.
Yes, keep pounding on that "buffer". Maybe it will come into existence by just believing in it.
I for one am done with this here.
Assignee | ||
Comment 24•11 months ago
|
||
(Sorry for the delay to put the patch, my Linux environment was broken, and not yet recovered/rebuilt.)
Comment 25•11 months ago
|
||
It looks like the original reporter of this bug has posted this vulnerability publicly, on October 17.
[Tracking Requested - why for this release]: sec-moderate, but the issue has been made public and there's a clear path to fixing this, so we want to get it out in a timely fashion.
Updated•11 months ago
|
Updated•11 months ago
|
Assignee | ||
Comment 26•11 months ago
|
||
Even though there is a reasonable usabe like bug 1567160, we should
ensure that AutoCopyListener
never update clipboard when the selection
change is caused by a Selection API call.
Assignee | ||
Comment 27•11 months ago
|
||
(I'll be offline Thu and Wed. Therefore, feel free to steal and/or land the patch. I think that it's enough safe to uplift even into ESR.)
Comment 28•11 months ago
|
||
A repro for mstange's question for macOS behavior in Phab:
- Open the attached file
- Select some random text in the lorem ipsum paragraph
- Soon after that
consetetur
will be selected by the script - Tap "Firefox Nightly -> Services -> Look Up in Dictionary"
- See which text shows up
Nightly: consetetur
The patch with JS_REASON: the random text you selected before the selection change
Comment 29•11 months ago
|
||
Pushed by masayuki@d-toybox.com: https://hg.mozilla.org/integration/autoland/rev/88e0043c5aa4 Make `AutoCopyListener` ignore any selection changes caused by Selection API r=karlt,smaug
Assignee | ||
Comment 30•11 months ago
|
||
Comment on attachment 9359698 [details]
Bug 1855345 - Make AutoCopyListener
ignore any selection changes caused by Selection API r=karlt!
Beta/Release Uplift Approval Request
- User impact if declined: The "primary" buffer is automatically updated by JS with a Selection API,
Selection.selectAllChildren()
. - Is this code covered by automated tests?: No
- Has the fix been verified in Nightly?: No
- Needs manual test from QE?: Yes
- If yes, steps to reproduce: 1. Run Firefox on Linux
- Type a word in the search bar or URL bar then, select it
- Open the testcase
- Middle click in the search bar or URL bar
Then, the middle click should paste the word you selected in the search bar or URL bar. Note that if you touch the test case, e.g., click in the test case, the primary buffer is still updated even after the patch is applied.
- List of other uplifts needed: None
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): The patch makes
AutoCopyListener
do nothing if it comes from a Selection API call. If some web apps use this path for updating the primary buffer without any malice, the feature would be broken, but it should be restored once bug 1567160 is fixed later. - String changes made/needed:
- Is Android affected?: Yes
ESR Uplift Approval Request
- If this is not a sec:{high,crit} bug, please state case for ESR consideration: This bug is really annoying because web apps can change the primary buffer content without users' operation. And this issue has already been published.
- User impact if declined: The "primary" buffer is automatically updated by JS with a Selection API,
Selection.selectAllChildren()
. - Fix Landed on Version:
- Risk to taking this patch: Low
- Why is the change risky/not risky? (and alternatives if risky): The patch makes
AutoCopyListener
do nothing if it comes from a Selection API call. If some web apps use this path for updating the primary buffer without any malice, the feature would be broken.
Assignee | ||
Updated•11 months ago
|
Comment 31•11 months ago
|
||
Updated•11 months ago
|
Comment 32•11 months ago
•
|
||
I am attempting to understand how to reproduce/verify this, but I am having trouble with it; @Masayuki Nakano can you clear up some confusion related to the steps in the uplift request?
- Regarding step 2: does it matter how the word is selected? Is primary selection not done a certain way? (CTRL+A? double-click? triple-click?)
- Regarding step 3: Which test case should we use? example.html or another?
- how do you suggest opening the test case? (I think we should definitely not copy-paste it considering it might erase the current primary selection) Does it matter?
- Regarding step 4: Should we perform the middle-click in a new tab? Or over the test case's URL?
Thank you for the help!
Updated•11 months ago
|
Updated•11 months ago
|
Comment 33•11 months ago
|
||
Comment on attachment 9359698 [details]
Bug 1855345 - Make AutoCopyListener
ignore any selection changes caused by Selection API r=karlt!
Approved for 120.0b4
Comment 34•11 months ago
|
||
uplift |
https://hg.mozilla.org/releases/mozilla-beta/rev/80eca9d3eeb0
Updated•11 months ago
|
Assignee | ||
Comment 35•11 months ago
|
||
(In reply to Daniel Bodea [:danibodea] from comment #32)
- Regarding step 2: does it matter how the word is selected? Is primary selection not done a certain way? (CTRL+A? double-click? triple-click?)
Either can update the primary selection. It's currently intended only for user interaction.
- Regarding step 3: Which test case should we use? example.html or another?
Attachment 9355227 [details] which is the original test case of this report. The script in the test case should not update selection when user does not interact with the document. In other words, currently, we unintentionally allow it updates primary selection when user mouse up on the document. It's traditional issue and out of scope of this bug.
- how do you suggest opening the test case? (I think we should definitely not copy-paste it considering it might erase the current primary selection) Does it matter?
Just open the test case into the new (active) tab. Either is fine if you don't break the primary selection. I used the history to open it.
- Regarding step 4: Should we perform the middle-click in a new tab? Or over the test case's URL?
Middle-click in the search bar or URL bar. Before applying the patch, the test case updates primary selection even during inactive. The patch avoids web apps to update primary selection with any API calls. However, the selection changes during mouse button down and mouse button up is used at mouse up because it's just not cared by the current design. Therefore, if you click in the test case, you keep seeing unexpected primary selection changes.
Sorry for my unclear comment and confused you.
Comment 36•11 months ago
|
||
Comment on attachment 9359698 [details]
Bug 1855345 - Make AutoCopyListener
ignore any selection changes caused by Selection API r=karlt!
Approved for 115.5esr
Comment 37•11 months ago
|
||
uplift |
https://hg.mozilla.org/releases/mozilla-esr115/rev/db1c17fea00f
Updated•11 months ago
|
Comment 38•11 months ago
|
||
Verified as fixed on Ubuntu 20.04 x64.
Comment 39•11 months ago
|
||
Setting Fx119 to wontfix and will reject the release-uplift request.
This will ride the trains with Fx120 and 115.5esr
Comment 40•11 months ago
|
||
Comment on attachment 9359698 [details]
Bug 1855345 - Make AutoCopyListener
ignore any selection changes caused by Selection API r=karlt!
See Comment 39
Comment 41•11 months ago
|
||
Verified as fixed on Firefox 115.5.0esr(Treeherder build) on Ubuntu 20.04 x64.
Comment 42•10 months ago
|
||
I guess the reporter removed themselves from the Bug Bounty program by deleting their account
Updated•10 months ago
|
Comment 43•10 months ago
|
||
Updated•10 months ago
|
Comment 44•10 months ago
|
||
Updated•10 months ago
|
Updated•10 months ago
|
Comment 45•5 months ago
|
||
Bulk-unhiding security bugs fixed in Firefox 119-121 (Fall 2023). Use "moo-doctrine-subsidy" to filter
Updated•4 months ago
|
Updated•3 months ago
|
Description
•