Sté*_*las 32 security options perl filenames
显然,运行:
perl -n -e 'some perl code' *
Run Code Online (Sandbox Code Playgroud)
或者
find . ... -exec perl -n -e '...' {} +
Run Code Online (Sandbox Code Playgroud)
(与-p
代替相同-n
)
或者
perl -e 'some code using <>' *
Run Code Online (Sandbox Code Playgroud)
经常在本网站上发布的 one-liners 中发现,具有安全隐患。这是怎么回事?如何避免?
Sté*_*las 38
首先,与许多实用程序一样,文件名以-
. 而在:
sh -c 'inline sh script here' other args
Run Code Online (Sandbox Code Playgroud)
其他参数传递给inline sh script
; 与perl
等价物,
perl -e 'inline perl script here' other args
Run Code Online (Sandbox Code Playgroud)
首先扫描其他 args 以获得更多选项到perl,而不是内联脚本。因此,例如,如果-eBEGIN{do something evil}
在当前目录中调用了一个文件,
perl -ne 'inline perl script here;' *
Run Code Online (Sandbox Code Playgroud)
(有或没有-n
)会做坏事。
与其他实用程序一样,解决方法是使用选项结束标记 ( --
):
perl -ne 'inline perl script here;' -- *
Run Code Online (Sandbox Code Playgroud)
但即便如此,它仍然很危险,这取决于/<>
使用的运算符。-n
-p
该问题在perldoc perlop
文档中进行了解释。
该特殊运算符用于读取输入的一行(一条记录,默认情况下记录为行),其中该输入来自依次传入的每个参数@ARGV
。
在:
perl -pe '' a b
Run Code Online (Sandbox Code Playgroud)
-p
意味着while (<>)
围绕代码的循环(此处为空)。
<>
将首先打开a
,一次读取一行记录,直到文件用完,然后再打开b
...
问题是,要打开文件,它使用了第一种不安全的形式open
:
open ARGV, "the file as provided"
Run Code Online (Sandbox Code Playgroud)
使用这种形式,如果参数是
"> afile"
,它afile
以书写模式打开,"cmd|"
,它运行cmd
并读取它的输出。"|cmd"
,您已打开一个流以写入cmd
.所以例如:
perl -pe '' 'uname|'
Run Code Online (Sandbox Code Playgroud)
不输出所调用文件的内容uname|
(顺便说一句,完全有效的文件名),而是输出uname
命令的输出。
如果您正在运行:
perl -ne 'something' -- *
Run Code Online (Sandbox Code Playgroud)
并且有人rm -rf "$HOME"|
在当前目录中创建了一个名为(又是一个完全有效的文件名)的文件(例如,因为该目录曾经可以被其他人写入,或者您提取了一个狡猾的存档,或者您运行了一些狡猾的命令,或者其他软件中的另一个漏洞被利用了),那么你就有大麻烦了。意识到该问题很重要的领域是在公共领域/tmp
(或可能被此类工具调用的工具)中自动处理文件的工具。
所谓的文件> foo
,foo|
,|foo
是一个问题。但在较小程度上< foo
,并foo
与前导或尾随ASCII间隔字符(包括空格,制表,换行,CR ...)以及该装置的那些文件将不被处理或错误之一将是。
还要注意某些多字节字符集中(如?
BIG5-HKSCS)中的某些字符以字节 0x7c 结尾,即|
.
$ printf ? | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Run Code Online (Sandbox Code Playgroud)
所以在使用该字符集的语言环境中,
perl -pe '' ./n?
Run Code Online (Sandbox Code Playgroud)
会尝试运行./n\x88
命令perl
将不会尝试在用户的语言环境来解释文件名!
AFAIK,您无法改变perl
系统范围内一劳永逸的不安全默认行为。
首先,问题仅出现在文件名的开头和结尾的字符上。所以,虽然perl -ne '' *
还是perl -ne '' *.txt
有问题,
perl -ne 'some code' ./*.txt
Run Code Online (Sandbox Code Playgroud)
不是因为现在所有的参数都./
以.txt
(所以不是-
, <
, >
, |
, space...)开始和结束。更一般地说,在glob 前面加上./
. 这也避免-
了-
使用许多其他实用程序调用或开始的文件的问题(在这里,这意味着您不再需要选项结束 ( --
) 标记)。
使用-T
开启taint
模式在一定程度上有帮助。如果遇到此类恶意文件,它将中止命令(仅适用于>
和|
情况,但不适用于<
或空白)。
当以交互方式使用此类命令时,这很有用,因为它会提醒您发生了一些不安全的事情。但是,这在进行一些自动处理时可能并不理想,因为这意味着有人可以通过创建文件来使该处理失败。
如果你确实想处理每个文件,不管它们的名字,你可以使用CPAN 上的ARGV::readonly
perl
模块(不幸的是,默认情况下通常没有安装)。这是一个非常短的模块,它执行以下操作:
Run Code Online (Sandbox Code Playgroud)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|"
例如转换为"< ./ foo|\0"
.
您可以BEGIN
在perl -n/-p
命令中的语句中执行相同的操作:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Run Code Online (Sandbox Code Playgroud)
在这里,我们根据./
正在使用的假设对其进行了简化。
但是,那个(和ARGV::readonly
)的副作用是$ARGV
inyour code here
显示了尾随的 NUL 字符。
perl
v5.21.5 及更高版本有一个新的<<>>
运算符,其行为类似于,<>
只是它不会进行特殊处理。参数将仅被视为文件名。因此,对于这些版本,您现在可以编写:
perl -e 'while(<<>>){ ...;}' -- *
Run Code Online (Sandbox Code Playgroud)
(不要忘记--
或使用./*
)而不必担心它会覆盖文件或运行意外命令。
-n
/-p
仍然使用危险的<>
形式。请注意符号链接仍在被遵循,因此这并不一定意味着在不受信任的目录中使用它是安全的。
除了@Stéphane Chazelas 的回答之外,如果我们使用-i
命令行选项,我们就不必担心这个问题:
$ perl -pe '' 'uname|'
Linux
$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.
Run Code Online (Sandbox Code Playgroud)
因为在使用-i
option 时,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.
Run Code Online (Sandbox Code Playgroud)