Perl 模棱两可的命令行选项,以及带有 -i 的 eval 的安全隐患?
Posted
技术标签:
【中文标题】Perl 模棱两可的命令行选项,以及带有 -i 的 eval 的安全隐患?【英文标题】:Perl ambiguous command line options, and security implications of eval with -i? 【发布时间】:2012-05-29 08:42:36 【问题描述】:我知道这是不正确的。我只是想知道 perl 是如何解析这个的。
所以,我在玩 perl,我想要的是 perl -ne
我输入的是 perl -ie
行为有点有趣,我想知道发生了什么。
$ echo 1 | perl -ie'next unless /g/i'
所以 perl Aborted (core dumped)
。阅读perl --help
我看到-i
需要扩展备份。
-i[extension] edit <> files in place (makes backup if extension supplied)
对于那些不知道-e
只是评估的人。所以我在想三件事中的一件可能会发生,要么它被解析为
perl -i -e'next unless /g/i'
i 得到 undef,其余的作为参数传递给 e
perl -ie 'next unless /g/i'
i 得到参数 e,其余的像文件名一样挂起
perl -i"-e'next unless /g/i'"
作为 i 的参数
当我跑步时
$ echo 1 | perl -i -e'next unless /g/i'
程序没有中止。这让我相信'next unless /g/i'
没有被解析为-e
的文字参数。毫无疑问,上面的内容会以这种方式解析,并且会产生不同的结果。
那是什么?好吧,再玩一点,我得到了
$ echo 1 | perl -ie'foo bar'
Unrecognized switch: -bar (-h will show valid options).
$ echo 1 | perl -ie'foo w w w'
... works fine guess it reads it as `perl -ie'foo' -w -w -w`
玩弄上面的,我试试这个...
$ echo 1 | perl -ie'foo e eval q[warn "bar"]'
bar at (eval 1) line 1.
现在我真的很困惑.. 那么 Perl 是如何解析这个的呢?最后,看起来你实际上可以从-i
中获得一个 Perl eval 命令。这有安全隐患吗?
$ perl -i'foo e eval "warn q[bar]" '
【问题讨论】:
看起来 Perl 正在吞噬-i
开关作为其参数之后的所有内容。
@JRFerguson 对我来说不是,看起来 perl 只是假设 -i 将所有字符占用到第一个空格,然后它似乎在其余部分运行自己的命令行解析形式输入。
确实是我要说的。
顺便说一句,没有安全隐患。 perl -e'...'
不能做任何你不能用 perl somefile.pl
做的事情。
但是,能够将参数设置为 perl -i
不应为您提供与将参数设置为 perl -e
或 perl $file
相同的能力。
【参考方案1】:
快速解答
Shell 引用处理正在折叠和连接它认为的所有参数。您的调用相当于
$ perl '-ienext unless /g/i'
它立即中止,因为 perl 将此参数解析为包含 -u
,这会触发核心转储,您的代码将开始执行。这是一个旧功能,曾经用于创建伪可执行文件,但如今它在本质上已经退化了。
对eval
的调用似乎是-e 'ss /g/i'
的错误解析。
第一条线索
B::Deparse 你的朋友可以吗,前提是你碰巧在没有dump
支持的系统上运行。
$ echo 1 | perl -MO=Deparse,-p -ie'next unless /g/i'
dump is not supported.
BEGIN $^I = "enext";
BEGIN $/ = "\n"; $\ = "\n";
LINE: while (defined(($_ = <ARGV>)))
chomp($_);
(('ss' / 'g') / 'i');
那么为什么unle
会消失呢?如果你运行的是 Linux,你可能还没有达到我的水平。上面的输出来自 Cygwin 上的 Perl,关于 dump
不受支持的错误是一个线索。
下一条线索
来自perlrun documentation的注意事项:
-u
此开关会导致 Perl 在编译您的程序后转储内核。然后,理论上您可以使用 undump 程序(未提供)将此核心转储并转换为可执行文件。这会以牺牲一些磁盘空间为代价来加快启动速度(您可以通过剥离可执行文件来最小化)。 (不过,在我的机器上,“hello world”可执行文件的大小约为 200K。)如果您想在转储之前执行程序的一部分,请改用
dump
运算符。注意:undump 的可用性是特定于平台的,可能不适用于特定的 Perl 端口。
工作假设和确认
Perl 的参数处理将整个块视为单个选项集群,因为它以破折号开头。 -i
选项使用下一个单词 (enext
),正如我们在 implementation for -i
processing 中看到的那样。
case 'i':
Safefree(PL_inplace);
[Cygwin-specific code elided -geb]
const char * const start = ++s;
while (*s && !isSPACE(*s))
++s;
PL_inplace = savepvn(start, s - start);
if (*s)
++s;
if (*s == '-') /* Additional switches on #! line. */
s++;
return s;
对于备份文件的扩展名,来自 perl.c 的上述代码最多使用第一个空白字符或字符串结尾,以先到者为准。如果还有字符,第一个必须是空格,然后跳过它,如果下一个是破折号,那么也跳过它。在 Perl 中,你可以把这个逻辑写成
if ($$s =~ s/i(\S+)(?:\s-)//)
my $extension = $1;
return $extension;
那么,-u
、-n
、-l
和 -e
都是有效的 Perl 选项,因此参数处理会吃掉它们并留下无意义的选项
ss /g/i
作为-e
的参数,perl 将其解析为一系列划分。但在执行开始之前,古老的-u
导致 perl 转储内核。
意外行为
更奇怪的是,如果您在 next
和 unless
之间放置两个空格
$ perl -ie'next unless /g/i'
程序尝试运行。 Back in the main option-processing loop我们看到了
case '*':
case ' ':
while( *s == ' ' )
++s;
if (s[0] == '-') /* Additional switches on #! line. */
return s+1;
break;
额外的空格会终止对该参数的选项解析。证人:
$ perl -ie'next 废话 -garbage --foo' -e die 死于 -e 第 1 行。
但没有我们看到的额外空间
$ perl -ie'next 废话 -garbage --foo' -e die 无法识别的开关:-onsense -garbage --foo(-h 将显示有效选项)。
但是,如果有一个额外的空格和破折号,
$ perl -ie'next -unless /g/i' 不支持转储。
设计动机
正如 cmets 所指出的,逻辑是为了 harsh shebang (#!
) line constraints 而存在的,perl 会尽力解决这个问题。
解释器脚本
解释器脚本是一个启用了执行权限的文本文件,其第一行的格式为:
#! interpreter [optional-arg]
解释器必须是本身不是脚本的可执行文件的有效路径名。如果
execve
的文件名参数指定了一个解释器脚本,那么解释器将使用以下参数调用:interpreter [optional-arg] filename arg...
其中 arg... 是由
execve
的argv
参数指向的一系列单词。对于便携使用,optional-arg 应该不存在,或者指定为单个单词(ie,它不应包含空格)......
【讨论】:
如果 shell 认为这只是一个参数'-ienext unless /g/i'
为什么 perl 会在空格上分解它?为什么 Perl 会假设如果 asdf jkl
作为一个参数发送,它应该被读作 -asdf
-jkl
? shell 不应该为 perl 做准备参数的工作,而 perl 只担心处理这些参数吗?
不幸的是,在这种情况下,在运行 Perl 5.14.2 的 Mac OS X (10.6.8) 上使用 Deparse
并没有显示出来。无论有没有它,都会得到“中止陷阱”。
@Evan Carroll,关于“为什么 perl 在空白处分解它?”我怀疑这与 Perl 对 #! 选项的处理有关。线。某些操作系统(例如 Linux)将命令之后的所有内容作为单个参数传递。分割空格将允许#!/usr/bin/perl -w -i
工作。
@Greg Bacon,我不会说 Linux 是一个古老的系统。
@ikegami 很有趣!我不知道shebang was executed so awkwardly 缺乏标准化。【参考方案2】:
要知道的三件事:
'-x y'
对 Perl 表示 -xy
(对于某些任意选项“x”和“y”)。
-xy
与 unix 工具一样,是一个代表 -x -y
的“捆绑包”。
-i
和 -e
一样,吸收了其余的论点。与-e
不同,它认为空格是参数的结尾(根据上面的#1)。
意思是
-ie'next unless /g/i'
这只是一种奇特的写作方式
'-ienext unless /g/i'
解绑到
-ienext -u -n -l '-ess /g/i'
^^^^^ ^^^^^^^
---------- ----------
val for -i val for -e
perlrun 文件-u
为:
此开关会导致 Perl 在编译程序后转储内核。然后,理论上您可以使用 undump 程序(未提供)将此核心转储并转换为可执行文件。这会以牺牲一些磁盘空间为代价来加快启动速度(您可以通过剥离可执行文件来最小化)。 (不过,在我的机器上,“hello world”可执行文件的大小约为 200K。)如果您想在转储之前执行程序的一部分,请改用 dump() 运算符。注意:undump 的可用性是特定于平台的,可能不适用于特定的 Perl 端口。
【讨论】:
注意:我不知道#3,我忘记了#1,所以这确实是一个非常棘手的问题,特别是因为简单地添加-MO=Deparse
对这个系统没有帮助。
'-x y' means -xy to Perl (for some arbitrary options "x" and "y").
这是真的吗? perl '-w edie'
不起作用。也许你的意思是'-x y' means -xy to Perl (for some arbitrary options "x" that requires an argument, and an arbitrary argument "y").
,就像perl '-e die'
的情况一样
@Evan Carroll,啊,所以它是特定于 -i 的?奇怪!以上是关于Perl 模棱两可的命令行选项,以及带有 -i 的 eval 的安全隐患?的主要内容,如果未能解决你的问题,请参考以下文章