Skip to content

Improve prevent-clipboard-write.js - #3959

Open
ryanbr wants to merge 3 commits into
gorhill:masterfrom
ryanbr:improve-prevent-clip
Open

Improve prevent-clipboard-write.js#3959
ryanbr wants to merge 3 commits into
gorhill:masterfrom
ryanbr:improve-prevent-clip

Conversation

@ryanbr

@ryanbr ryanbr commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Add navigator.clipboard.write() coverage to prevent-clipboard-write

The current scriptlet covers navigator.clipboard.writeText() and document.execCommand('copy'|'cut') but leaves navigator.clipboard.write() unhandled. While ClickFix sites predominantly use writeText() today, clipboard.write() is the more capable API and a natural fallback for attackers looking to evade detection.

The proposed addition intercepts clipboard.write() inside the existing installTraps block, reads the first MIME type from each ClipboardItem (always text/plain for ClickFix payloads), and passes the extracted text through the existing prevent() function — so pattern matching, domAlert, and logging all behave identically to the writeText path. A .catch() is included to ensure context.reflect() is called on any blob read failure, avoiding silent clipboard breakage on legitimate sites.

@ryanbr
ryanbr force-pushed the improve-prevent-clip branch from efdc072 to bc16084 Compare August 9, 2026 09:33
@ryanbr

ryanbr commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@gorhill

gorhill commented Aug 9, 2026

Copy link
Copy Markdown
Owner

I don't see at these links how the clipboard write occurs.

@ryanbr

ryanbr commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

The clipboard code is in the images attached to the Unit42 X post — they don't render without login, so attaching the relevant clickfix one here:

HNX-MtaXgAAEurE

The obfuscated script tries several write routes in turn: navigator.clipboard, window['clipboardD' + 'ata'] (legacy branch, inert in modern browsers), then a document.queryCommandSupported/execCommand fallback.

That fallback is what the setData commit targets: a copy event handler calls e.clipboardData.setData('text/plain', payload) + e.preventDefault(), then the page fires execCommand('copy'). The existing execCommand trap only inspects the selection, so the payload never reaches prevent(). Trapping DataTransfer.prototype.setData closes that route, and since native setData() returns undefined, the interception isn't detectable by the page.

To get around clipboard hacks used by clickfix

Clipboard write route Upstream master This PR
navigator.clipboard.write([ClipboardItem]) ❌ Not covered ✅ Trapped, reads text/plain blob; guards empty types
Copy-event injection — e.clipboardData.setData(...) + preventDefault() + execCommand('copy') ❌ Not covered ✅ Trapped via DataTransfer.prototype.setData

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants