我怎样才能保护自己免受这种剪贴板滥用?

信息安全 开发 Unix 网页浏览器 剪贴板
2021-08-21 22:56:45

来自网站的剪贴板滥用

许多网站在从页面复制信息时,都会使用 JavaScript 或 CSS 悄悄地在用户的剪贴板中插入或替换文本。据我所知,这主要用于广告目的,但已经证明了用于漏洞利用的 PoC。

然而我发现,一个人甚至不需要 JS 或 CSS 来制作一个在粘贴到终端时会产生恶意影响的漏洞利用。粘贴隐藏的退格字符可以改变 shell 命令的整体含义。在基于术语的编辑器中粘贴也不安全。Esc然后粘贴:!会导致正在运行的 Vim 实例执行 shell 命令。粘贴^X^C将退出 Emacs 和/或什至cat. 粘贴^Z将主要停止任何基于术语的编辑器并返回到 shell。

更糟糕的是,许多受信任的网站不会清理这些不可打印的字符。Twitter 过滤掉Esc但不退格。Pastebin.com 似乎没有过滤掉任何东西。Stack Exchange也没有,因此以下漏洞利用(警告:恶意代码,请勿复制并粘贴到 Unix 终端!!)很可能被制作成更糟糕的东西,更有可能被受害者粘贴:

回声??'.?!?:?? ?ke?S? ?i3?l?ld?K?o? -1?+?9 +?2?-1'? >? ?/?t?m?p?/?l?o?l?
回声??':!.? ke?S? ?i3?l?ld?K?o? -2?+?9 +?7?-1'? ?>?>? ?/?t?m?p?/?l?o?l?
回声??'.?:?!?? ?ke?S? ?i3?l?ld?K?o? -3?+?9 +?4?-1'? ?>?>? ?/?t?m?p?/?l?o?l?
睡觉 1
md5sum /tmp/lol

编辑:原始退格现在由 Stack Exchange 过滤,所以这个 PoC 需要 转义。/编辑

以下是 Chrome 的渲染方式:

来自 Chrome 的屏幕截图

Firefox 没有那么容易被愚弄,但仍然对 JS 或 CSS 方法视而不见:

火狐的截图

当粘贴到终端时,它只会杀死所有用户的进程。

该怎么办?

这基本上告诉我的是,我永远不应该从网页复制任何内容并将其粘贴到终端应用程序中。嗯,太好了。我的工作环境基本上是 1 个网络浏览器和 40 个终端窗口/选项卡。我一直在复制和粘贴代码片段。

现在,有没有人可以保护我免于自己的坏习惯(老实说,我不认为那是坏习惯)?浏览器厂商?终端厂商?剪贴板系统供应商?可能是第三方应用程序?

4个回答

您可能已经猜到了这一点,但永远不要使用终端粘贴功能将内容粘贴到 vim/emacs中。这就像向编辑器发送一批命令,可以做任何事情。

由于这些原因,编辑器有自己的复制粘贴功能,无法注入。例如,在 vim 中,您应该使用+寄存器与系统剪贴板交换数据("+p用于粘贴)。

关于 shell 或其他终端应用程序:已经确定,您不得将不安全的数据粘贴到您的终端中。

zsh有一个安全粘贴插件,它可以防止代码在粘贴时实际运行,但无论如何已经有人利用了它

此外,在 apple.se 上提出了一个类似的问题(关于意外粘贴)。大多数解决方案也可能对您有用。

更新:在 vim 中,如果set mouse=a使用,用鼠标中键粘贴是安全的。不过,您仍然可以使用 shift-Insert 进行粘贴。

请注意,从版本 292 开始,xterm 删除了 ASCII 控制字符,除了\b, \r, \t, DEL(0x7f) 和\n(它像其他终端一样转换\n\rs),您可以将它们与allowPasteControls资源一起带回。自 2015 年 10 月起gnome-terminal, VTE terminator( ...使用的终端仿真器库xfce-terminal)也开始使用

因此,在那些终端中,^C, ^[, ^D, ^Z, ^\, ^U,^W不再是问题,但 DEL, \b, (对于完成可以扩展命令替换\t的某些配置(包括默认配置)非常危险),并且仍然存在。zsh\r\n

xterm 还有一些粘贴模式可以在这里提供帮助。

  • 在某些zsh 或 vim安全粘贴插件\e[?2004h中使用的序列启用括号粘贴模式

    zsh( zle) 自 5.1 和bash( readline) 自 4.4 现在支持内置。虽然默认情况下在zsh中启用它,但您需要使用bind 'set enable-bracketed-paste on'in手动启用它bash(或在 or 中的配置readline)。~/.inputrc/etc/inputrc

    这个包装了 和 之间的\e[200~选择\e[201~

    大多数其他现代终端,​​如基于 VTE 的终端 ( gnome-terminal, xfce-terminal, terminator...), rxvt, konsole, OS/XTerminal现在也支持该终端。

    但是,在某些其他终端(或其版本)中(或带有allowPasteControlsin xterm),存在缺陷,\e[201~可能出现在选择中,并将被视为右括号。

    这可以通过括号来解决\e\e[201~\e[200~201~,但是AFAIK还没有由任何终端仿真器完成(这意味着应用程序会看到几个粘贴)。

    ^C//如果没有在 tty 线路规程中禁用,^Z也会^\导致信号被发送到终端的前台进程组。ISIG

  • 使用序列启用引用的粘贴模式\e[?2005h(使用 禁用\e[?2005l)。

    这个在每个字符(实际上是字节)前面加上一个^V字符。

    ^V是规范模式下 tty 行规则的默认lnext文字 next)字符,并且也被vi其他编辑器和一些行编辑器(如 readline 或zsh's zle)识别为这样。

    那个与上面的括号模式没有相同的问题,并且有利于终端规范模式(比如当你这样做时cat > file)和其他一些应用程序,但有一些缺点:

    • 换行符和 CR 最终呈现为^M. 这可以通过另一个转义序列来避免:\e[?2006h,但这会导致换行符作为 NUL 字符插入vim并在终端规范模式下显示为^J(除非你这样做stty -echoctl)(尽管这只是一个外观问题)。
    • zle这对于没有正确插入的多字节字符不起作用vim
    • 一些可视化应用程序不能^V文字那样处理 next,因此您可能仍然需要有选择地关闭它。
    • 您不能在其中使用它,vim例如^V 1不插入1^A在那里。
    • 除了支持它之外,我不知道有任何其他终端xterm,但是我还没有进行过广泛的调查。
  • 它还允许您通过配置定义自己的括号粘贴模式。例如,使用:

     XTerm*allowPasteControls: true
     XTerm.VT100.translations: #override \
       Ctrl Shift<KeyPress> Insert: \
         insert-formatted("\033[202~%S~%s", CLIPBOARD,PRIMARY,CUT_BUFFER0)'
    

    插入CLIPBOARD// PRIMARY_ 然后应用程序可以可靠地解释它(尽管它仍然需要在 tty 行规则中禁用)。CUT_BUFFER0^[[202~<size-in-bytes>~<content>Shift+Ctrl+InsertISIG

另一种方法是使用伪 tty 包装器,^V仅在控制字符前面插入这些包装器。这样的包装器应该能够以一定的可靠性检测粘贴中的控制字符,因为真正的键盘按键一次只会发送一个字符或以 ESC 开头的一系列字符,而粘贴一次会发送几个。

您仍然会遇到^J终端规范模式或^@in 中显示换行符的问题vim,但这可以通过 shell 的一些合作来解决

概念证明:

例如用作:

./safe-paste bash

在该包装下启动一个bash外壳。

#!/usr/bin/perl

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

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

  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;

  exec(@ARGV);
  die "Cannot exec(@ARGV): $!";
}
$pty->close_slave();

$SIG{WINCH} = sub {
  $pty->slave->clone_winsize_from(\*STDIN);
};

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);
  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)}

更好的方法可能是使用剪贴板管理器,您可以在其中指定粘贴模式并标记潜在危险的选择。

好吧,事实证明,我目前的剪贴板方法很擅长缓解这种情况。

在选项卡之间复制粘贴片段时,我只是正常复制粘贴。

但是,当复制粘贴到终端/PuTTY 会话时,我(有点反对在终端中编辑文本)通常在 Notepad++ 或 Emacs 中组装它(取决于操作系统),然后将最终文本复制粘贴到终端中. 两个编辑器都显示控制字符(和其他不可打印的字符),因此很容易注意到那里的任何欺骗行为。

不幸的是,出于安全原因,我不能声称我使用中间文本编辑器方法,这是因为我还不擅长 vim 或任何其他基于终端的编辑器。

我可以声称任何复制和粘贴代码片段都是一个坏习惯,但这是在回避问题。我个人输入这些代码元素而不是复制它们,但那是因为我通常想更改其中的一些内容,或者学习如何完成手头的任务;或者我只是一个狂热的疯子。

您可以做的是自动清理剪贴板内容后台应用程序可能会不断监视剪切缓冲区的内容并删除控制字符;我不确定 X11 是否可以被哄骗发送一个剪切缓冲区更改的事件,但每秒轮询 10 次就可以了。X11 对偶性(剪切缓冲区与选择)会使事情变得更复杂,但我相信这是可以做到的(而且,我相信可以做到)。

消毒内容可能很棘手。例如,假设您删除了 0..31 范围内的所有字节(ASCII 控制字符),但换行符 (10)、回车符 (13) 和制表符 (9) 除外。然后,如果我写这个(Linux系统):

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

然后我将它粘贴到一个以 UTF-8 模式vim运行的实例中xterm,然后我杀死我所有的进程......尽管剪切缓冲区在任何时候都不会包含任何“不需要的”控制字符。该序列C0 9B不是有效的 UTF-8,但足够接近,因此xterm无论如何都会尝试对其进行解码,并且它会解码为0x1B, aka Escape ... 其他棘手的序列包括E0 80 9B. 请注意,虽然有效的 UTF-8从不包含 value 的字节C0,但它可能包含 value 的字节E080并且9B(但不按该顺序)。因此,消毒过程最好彻底而严格。

这种工具的一个附加功能是自动将 CR+LF 序列和单独的 CR 转换为 LF。可能将 128..159 范围内的血腥 CP-1252 角色转换为理智的标准角色。这不仅是一个安全问题;在非恶意情况下,它可能是一个有用的工具。