运行perl -ne'...'的安全隐患*


27

显然,正在运行:

perl -n -e 'some perl code' *

要么

find . ... -exec perl -n -e '...' {} +

(与相同,-p而不是-n

要么

perl -e 'some code using <>' *

通常在此网站上发布的单行代码中发现,具有安全隐患。这是怎么回事?如何避免呢?

Answers:


33

有什么问题

首先,像许多实用程序一样,您会遇到文件名以开头的问题-。而在:

sh -c 'inline sh script here' other args

其他args传递给inline sh script; 与perl等效,

perl -e 'inline perl script here' other args

扫描其他args以查找更多选项,以便首先执行perl,而不是内联脚本。因此,例如,如果-eBEGIN{do something evil}当前目录中有一个名为

perl -ne 'inline perl script here;' *

(使用或不使用-n)会做一些邪恶的事情。

与其他实用程序一样,解决方法是使用选项结束标记(--):

perl -ne 'inline perl script here;' -- *

但是即使那样,仍然很危险,这取决于/ <>所使用的运算符。-n-p

perldoc perlop文档中解释了该问题。

该特殊运算符用于读取输入的一行(一条记录,默认情况下为记录),该输入依次来自传入的每个参数@ARGV

在:

perl -pe '' a b

-p意味着while (<>)围绕代码的循环(此处为空)。

<>将首先打开a,一次读取记录一行,直到文件耗尽,然后打开b...

问题在于,要打开文件,它将使用以下第一种不安全的形式open

open ARGV, "the file as provided"

在这种形式下,如果参数为

  • "> afile",它将afile以写入模式打开,
  • "cmd|",它将运行cmd并读取其输出。
  • "|cmd",您可以打开一个流,以写入的输入cmd

因此,例如:

perl -pe '' 'uname|'

不输出称为uname|(完全有效的文件名btw)的文件的内容,而是uname命令的输出。

如果您正在跑步:

perl -ne 'something' -- *

有人rm -rf "$HOME"|在当前目录中创建了一个文件(再次是完全有效的文件名)(例如,因为该目录曾经可由其他人写入,或者您提取了一个躲闪的存档,或者运行了一个躲闪的命令,或者利用了其他软件中的另一个漏洞),那么您就遇到了大麻烦。意识到该问题很重要的领域是诸如在公共区域自动处理文件的/tmp工具(或此类工具可能调用的工具)。

所谓的文件> foofoo||foo是一个问题。但是在较小程度上,< foo并且foo具有ASCII空格字符的前导或尾随字符(包括空格,制表符,换行符,cr ...),这意味着这些文件将不会被处理或将被错误处理。

还请注意,某些多字节字符集中的某些字符(例如ǖBIG5-HKSCS中的字符)以字节0x7c结尾,即的编码|

$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000  88  7c
        210   |
0000002

因此,在使用该字符集的语言环境中,

 perl -pe '' ./nǖ

会尝试运行./n\x88命令perl不会尝试在用户的语言环境来解释文件名!

如何修复/解决

AFAIK,您无法更改perl整个系统范围内一次不安全的默认行为。

首先,仅在文件名的开头和结尾使用字符会出现问题。所以,尽管perl -ne '' *还是perl -ne '' *.txt有问题,

perl -ne 'some code' ./*.txt

是不是因为所有的争论,现在开始./和结束的.txt(所以不-<>|,空间...)。更一般地,这是一个好主意,前缀水珠./。这也避免了文件调用--以许多其他实用程序开头的问题(在这里,这意味着您不再需要end-of-options(--)标记)。

使用-T打开taint模式在一定程度上有所帮助。如果遇到这样的恶意文件(仅适用于>|情况,不包含<空格),它将中止命令。

当以交互方式使用此类命令时,此功能很有用,因为它会提醒您发生了一些躲闪的事情。但是,当执行某些自动处理时,这可能并不理想,因为这意味着某人仅通过创建文件就可以使处理失败。

如果确实要处理每个文件,无论它们的名称如何,都可以在CPAN上使用ARGV::readonly perl模块(不幸的是,默认情况下通常未安装)。这是一个非常简短的模块,它可以:

sub import{
   # Tom Christiansen in Message-ID: <24692.1217339882@chthon>
   # reccomends essentially the following:
   for (@ARGV){
       s/^(\s+)/.\/$1/;   # leading whitespace preserved
       s/^/< /;       # force open for input
       $_.=qq/\0/;    # trailing whitespace preserved & pipes forbidden
   };
};

基本上,它通过将" foo|"例如变为来清除@ARGV "< ./ foo|\0"

您可以BEGINperl -n/-p命令中的语句中执行相同的操作:

perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*

在这里,我们根据使用的假设对其进行简化./

该(和ARGV::readonly)的副作用是$ARGVin your code here表示尾随NUL字符。

更新2015-06-03

perlv5.21.5及更高版本具有一个新的<<>>运算符,<>除了不会进行特殊处理外,其行为类似于。参数将仅被视为文件名。因此,使用这些版本,您现在可以编写:

perl -e 'while(<<>>){ ...;}' -- *

(不要忘了the --或use ./*)而不必担心它会覆盖文件或运行意外命令。

-n/ 虽然-p仍然使用危险<>形式。并且要注意符号链接仍在遵循中,因此并不一定意味着在不受信任的目录中使用是安全的。


2
你整天都在工作,我打赌。做得好。
mikeserv

2
对perl进行了很好的更新,但是奇怪的是perl开发人员没有添加-P和-N选项来使用它(无法更改现有的-p和-n,因为某些脚本可能依赖于不安全的行为)
cas

9

除了@StéphaneChazelas的答案之外,如果我们使用-i命令行选项,我们也不必担心此问题:

$ perl -pe '' 'uname|'
Linux

$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.

因为当使用-ioption时,perl使用stat在处理文件之前先检查文件状态:

$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40)                = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.

1
stat检查和紧随其后的有效perl处理之间是否存在可能的竞争条件?
Totor

@Totor:我想不。
cuonglm

没关系stat。仅仅-i是在适当的位置编辑文件,所以接受除实际文件路径以外的参数没有任何意义,因此使用时-i,不需要进行特殊处理。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.