Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

Clipboard abuse from websites

Many websites use JavaScript or CSS to stealthily insert or replace text in the user's clipboard whenever they copy information from the page. As far as I know this is mostly used for advertising purposes, but PoC for exploits have been demonstrated.

However I discovered that one does not even need JS or CSS to craft an exploit that has malicious effects when pasted in a terminal. Pasting hidden backspace characters can change the whole meaning of a shell command. Pasting in a term-based editor isn't safe either. Pasting Esc then :! can cause a running Vim instance to execute a shell command. Pasting ^X^C will quit Emacs and/or even cat. Pasting ^Z will stop mostly any term-based editor and return to the shell.

What makes it worse is that many trusted websites do not sanitise these non-printable characters. Twitter filters out Esc but not backspace. Pastebin.com doesn't appear to filter out anything. Neither does Stack Exchange, hence the following exploit (WARNING: malicious code, DO NOT copy and paste into a Unix terminal!!) that could very well be crafted into something worse and more likely to be pasted by a victim:

echo '.!: keS i3l ldKo -1+9 +2-1' > /tmp/lol
echo ':!. keS i3l ldKo -2+9 +7-1' >> /tmp/lol
echo '.:! keS i3l ldKo -3+9 +4-1' >> /tmp/lol
sleep 1
md5sum /tmp/lol

Edit: The backspaces are now filtered by Stack Exchange, so this PoC does not work here anymore. /Edit

Here is how Chrome renders it:

Screenshot from Chrome

Firefox isn't fooled as easily, but still remains oblivious to the JS or CSS approach:

Screenshot from Firefox

And when pasted into a terminal, it just kills all the user’s processes.

What to do?

What this basically tells me is that I should never, ever, copy anything from a web page and paste it into a terminal application. Well, great. My work environment is basically 1 web browser and 40 terminal windows/tabs. I copy and paste code snippets all the time.

Now, is there anyone who can protect me from my own bad habits (which, honestly, I don’t think are that bad)? Browser vendors? Terminal vendors? Clipboard system vendors? A third-party application maybe?

share|improve this question
Great question! I don't really have any convenient answer for you, short of what I'm doing - using an intermediary text editor (set to display all non-printable characters) for clipboard operations and inspect actual copied contents there before pasting it somewhere like a terminal window, or even into files to compile. It becomes your second nature in a while, and I suggest always having one instance of your favorite text editor running for such tasks. One of the first things you learn being involved with IT security is there is no such thing as WYSIWYG. ;) –  TildalWave Jul 18 '13 at 0:49
@Simon - This question is a fair bit more specific and that possible duplicate question sadly doesn't provide to this question here relevant answers. Make users inadvertently copy something they didn't intend to can be achieved by other means than by resorting to JavaScript. For example, there could be a 1x1 pixel DIV with CSS properties set to match its color to background color of text and a hidden overflow between any of the two words in some text, or even individual letters. What I'm saying is, this question is also about exploiting visual representations of texts, not just clipboard. ;) –  TildalWave Jul 18 '13 at 0:56
+1 for great question. –  asdasdasd Jul 18 '13 at 0:58
The problem with protecting is that you at some times want to copy/paste such characters. Else I would tend to go for an approach to use the clipboard to sanitize the input. –  Uwe Plonus Jul 18 '13 at 6:06
@Simon that other question is about websites reading the user’s clipboard, which should never happen anyway. My question is about websites writing to the clipboard, which unfortunately happens all the time. –  Sam Hocevar Jul 18 '13 at 6:28

5 Answers 5

You might have guessed this, but never use the terminals pasting functionality to paste things into vim/emacs. It's like sending a batch of commands to the editor, that can do anything.

For these reasons, editors have their own copy-pasting functionality, which cannot be injected. For instance, in vim, you should use the + register to exchange data with the system clipboard ("+p for pasting).

Regarding the shell or other terminal applications: It has been established, that you must not paste unsafe data into your terminal.

There is a safe-paste plugin for zsh, which prevents code from actually running when pasted, but someone has already exploited it anyways.

Also, a similiar question (about accidental pasting) has been asked on apple.se. Most of the solutions might also work for you.

Update: In vim, if set mouse=a is used, pasting with the middle mouse button is safe. You can still paste with shift-Insert though.

share|improve this answer
With "+p, that assumes a version of vim built with X support, and that vim has access to the X display that holds the relevant X selection (for instance, it wouldn't work after a ssh (without -X/-Y)). –  Stéphane Chazelas Mar 4 '14 at 12:52
That safe-paste zsh plugin is safe with recent versions of xterm (since 292) in its default configuration since xterm now discards most control characters (including ESC). Note that it only works for the zsh prompt, not for other applications (though some applications like vim could also be extended to do something similar) –  Stéphane Chazelas Mar 4 '14 at 12:57
The topic of secure copy/pasting has been treated by the Qubes OS team. Their conclusion is to validate/white-list all the data that goes through a clipboard, and that is what you need to do whenever two mutually distrusting principals need to interact. Might be worth mentioning that rule of thumb. –  Steve DL May 11 at 15:57

Well, it turns out that my current approach to clipboarding is good at mitigating this.

When copy pasting snippets between tabs, I just copy paste normally.

However, when copy pasting into a terminal/PuTTY session, I (being a bit averse to editing the text in the terminal), usually assemble it in Notepad++ or Emacs (depending on OS) and then copy-paste the final text into the terminal. Both editors show control characters (and other non-printable characters), so it's easy to notice any skullduggery there.

I unfortunately can't claim that I use the intermediate-text editor approach for security reasons, it's because I'm not yet adept at vim or any other terminal-based editor.

share|improve this answer
But then, there might be paste exploits in the editors as well, as others have pointed out. –  HRJ Nov 27 '14 at 6:21

I could claim that any copy&paste of code snippets is a bad habit, but that's side-stepping the issue. I personally type such code elements instead of copying them, but that's because I usually want to change some things in them, or learn how to do the task at hand; or maybe I am just a raving maniac.

What you could do is to automatically sanitize clipboard contents. A background application may constantly monitor cut buffer contents and remove control characters; I am not sure that X11 can be coaxed into sending an event for a cut buffer change, but polling 10 times per second would do the trick. The X11 duality (cut buffers vs selections) will make things a bit more complex but I believe this can be done (and, moreover, I believe you can do it).

Sanitizing contents can be tricky. For instance, suppose that you remove all bytes in the 0..31 range (the ASCII control characters) except newline (10), carriage return (13) and tabulations (9). Then, if I write this (Linux system):

printf "\xC0\x9B:!kill -9 -1\n" | xclip

and then I paste that into a vim instance running in xterm (in UTF-8 mode), then I kill all my processes... though the cut buffer never contains any "unwanted" control character at any point. The sequence C0 9B is not valid UTF-8, but close enough so that xterm will try to decode it anyway, and it decodes to 0x1B, aka Escape... other tricky sequences include E0 80 9B. Note that while valid UTF-8 never includes a byte of value C0, it may contain bytes of value E0, 80 and 9B (but not in that sequence). The sanitizing process thus had better be thorough and strict.

An added functionality of such a tool would be to automatically convert CR+LF sequences, and lone CR, into LF. Possibly convert the bloody CP-1252 characters from the 128..159 range into their sane, standard counterparts. This is not only a security issue; it could be a useful tool in non-malicious situations.

share|improve this answer
This is crazy! :P Imagine having a random process altering the clipboard! –  droope Nov 1 '13 at 0:40
this should be implemented as an option for Klipper –  Ohad Cohen Nov 1 '13 at 1:46
Note that xterm already removes all ASCII ctrl (0x0 to 0x31) except \b (unfortunately), \r, \n and \t (unless you tell it not to with the allowPasteControls resource). –  Stéphane Chazelas Mar 2 '14 at 22:01
It seems it's vim, not xterm that turns C0 9B to 1B. –  Stéphane Chazelas Mar 4 '14 at 12:10

I solved this problem for myself recently by running my web browser in a VirtualBox virtual machine. The x selection is not sync'd between the VM and the host so it's no longer possible for me to mindlessly middle-click paste stuff from the web into a terminal.

share|improve this answer

Note that as of version 292, xterm removes ASCII control characters except \b, \r, \t, DEL (0x7f) and \n (it converts \n to \rs like other terminals), and you can bring them back with the allowPasteControls resource.

So ^C, ^[, ^D, ^Z, ^\, ^U, ^W are no longer a problem but DEL, \t (very dangerous with some configurations of zsh where completion can expand command substitutions), \r and \n still are.

xterm also has a couple of paste modes that can help here.

  • the bracketed paste mode enabled with the \e[?2004h sequence as used in some zsh or vim safe-paste plugins.

    This one wraps the selection between \e[200~ and \e[201~.

    Some other terminals like gnome-terminal also support that one.

    In those other terminals though (or with allowControlPaste), that's flawed in that \e[201~ may appear in the selection, and would be taken as the closing bracket. That could be fixed by bracketing like \e\e[201~\e[200~201~, but it's not done my any terminal emulator yet AFAIK.

  • The quoted paste mode enabled with the \e[?2005h sequence (disabled with \e[?2005l).

    This one prepends every character (actually byte) with a ^V character.

    ^V is the default lnext (literal next) character of the terminal in canonical mode, and is also recognised as such by vi and other editors and some line editors like readline or zsh's zle.

    That one doesn't have the same problem as the bracketed mode above, and has the benefit to work for the terminal canonical mode (like when you do cat > file) and a few other applications but has a few drawbacks:

    • newline and CR end up being rendered as ^M. That can be avoided with another escape sequence: \e[?2006h, but that causes the newlines to be inserted as NUL characters in vim and show up as ^J (unless you do stty -echoctl) in the terminal canonical mode (though it's only a cosmetic issue).
    • That doesn't work great for multi-byte characters which are not inserted properly in zle or vim for instance.
    • some visual applications don't handle ^V as literal next, so you may still have to turn it off selectively.
    • you can't use it in vim as ^V 1 for instance doesn't insert 1 but ^A there.
    • I'm not aware of any other terminal beside xterm supporting it, but then I've not done an extensive survey.

Another approach would be to use a pseudo-tty wrapper that inserts those ^V only in front of control characters. Such wrapper should be able to detect control characters in pastes with some reliability because real keyboard keypresses would only send one character at a time or a sequence of characters starting with ESC, while pastes would send several at a time.

You'd still have the problem of newlines shown as ^J in the terminal canonical mode or ^@ in vim, but that could be worked around with with some cooperation by the shell

A proof of concept:

To be used for instance as:

./safe-paste bash

To start a bash shell under that wrapper.


use IO::Pty;
use IO::Stty;

my $pty = new IO::Pty;
my $pid = fork();
die "Cannot fork" if not defined $pid;
unless ($pid) {
  my $slave = $pty->slave();
  close $pty;

  open(STDIN,"<&". $slave->fileno())
    or die "Couldn't reopen STDIN for reading, $!\n";
  open(STDOUT,">&". $slave->fileno())
    or die "Couldn't reopen STDOUT for writing, $!\n";
  open(STDERR,">&". $slave->fileno())
    or die "Couldn't reopen STDERR for writing, $!\n";

  close $slave;

  die "Cannot exec(@ARGV): $!";

$SIG{WINCH} = sub {
  kill WINCH => $pid if $pid;
  print "STDIN terminal size changed.\n";
  $SIG{WINCH} = \&winch;

my $old = IO::Stty::stty(\*STDIN, '-g');
IO::Stty::stty(\*STDIN, 'raw', '-echo');
$tty = fileno($pty);
my ($rin,$ein) = ('','','');
vec($rin, 0, 1) = 1;
vec($rin, $tty, 1) = 1;
vec($ein, $tty, 1) = 1;
my ($to_stdout, $to_tty) = ('', '');
my $eof;
$SIG{CHLD} = sub {$eof = 1};
until ($eof && $to_stdout eq '' && $to_tty eq '') {
  my ($rout,$wout,$eout,$timeleft);
  my $win = '';
  vec($win, 0, 1) = 1 if ($to_stdout ne "");
  vec($win, $tty, 1) = 1 if ($to_tty ne "");
  ($nfound,$timeleft) = select($rout=$rin,$wout=$win,$eout=$ein,undef);
  die "select failed: $!" if ($nfound < 0);
  if ($nfound > 0) {
    if (vec($eout, $tty, 1)) {
      print STDERR "Exception on $tty\n";
    if (vec($rout, 0, 1)) {
      my $buf;
      if (sysread(STDIN, $buf, 4096)) {
        if ($buf =~ /.[\0-\037\177]/ || $buf =~ /^(?:[\0-\032\034-\037]|\033.*?[~a-zA-NP-Z])./) {
          $buf =~ s/[\0-\037\177]/\026$&/g;
          # TODO: add UTF-8 sanitizing
          $buf =~ y/\r/\n/;
        $to_tty .= $buf;
      } else {
        $eof = 1;
        vec($rin, 0, 1) = 0;
    if (vec($rout, $tty, 1)) {
      my $buf;
      if (sysread($pty, $buf, 4096)) {
        $to_stdout .= $buf;
      } else {
        $eof = 1;
        vec($rin, $tty, 1) = 0;
        $to_tty = '';
    if ($to_tty ne '' && vec($wout, $tty, 1)) {
      my $written = syswrite($pty, $to_tty);
      $to_tty = substr($to_tty, $written) if $written;
    if ($to_stdout ne '' && vec(wout, 1, 1)) {
      my $written = syswrite(STDOUT, $to_stdout);
      $to_stdout = substr($to_stdout, $written) if $written;
END{IO::Stty::stty(\*STDIN, $old)}

A better approach would probably be to use a clipboard manager where you can specify the paste mode and that would flag potentially dangerous selections.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.