perl程序里面执行外部命令

Perl以复合语言著称,实际表现为能够很好地调用其他程序来为它协同工作,通过它们的输出来协调运行情况,通过一种特别的方式把它们作为另一种形式的输入,传递给别的程序,从而确保每个环节能够顺利运行。正如Perl所告诉我们的,实现的方法不止一个。

exec()函数

执行外部程序或者执行一个系统命令我们只需要调用exec()函数便可。当Perl遇到exec()表达式的时候,便去寻找函数调用的参数,然后创建一个新的过程来执行指定的命令。Perl不会把控制权交还给原先调用exec()函数的程序,而是直接进入函数的过程。

另外还有一个类似的函数是system()函数。system()和exe()过程十分相似,它们之间唯一的区别就是,perl首先从初处理过程中分离出一个子程序来。这个子程序就是被送入系统的参数,然后初处理等待直至子程序运行,再执行剩余的其他程序。下面我们来进一步讨论system()函数的调用,但绝大部分的结论同样可以应用到exe()函数中。

system()函数

被输入到系统的参数是一个列表形式,列表的第一项是被调用执行的程序名称,而剩下的几项将作为参数被传递给该程序。尽管如此,如果只有一个参数的话,system()会异常运行。在这种情况下,Perl会扫描这个参数是否包含Shell元字符,如果有的话,它会把这些字符通过Shell来解释。因此Perl便产生了大量的内部命令来完成工作,否则Perl将拆分这些字符并调用更有效的C程序库,但是这些库也不能很好地识别这些内部命令字符。

现在假设我们有一个CGI表单需要用户名来列出一些文件(包含用户的统计数据表),我们就可以通过system()来调用CAT来实现我们的目的,代码如下:

system ("cat /usr/stats/$username")

而用户名$username则来自下面这个表单项里:

$username = param ("username");

用户填写表单用户名,比如jdimov,然后提交它。Perl却找不到任何元字符在“cat /usr/stats/jdimov”的字符串中,所以Perl就调用execvp()来运行“cat”,再返回到我们的脚本程序里来。这段脚本看上去没什么,其实是可以被别有用心的攻击者利用的。问题在于攻击者可以在表单里面使用特殊字符来实现任意Shell命令的执行。我们假设攻击者在用户名的地方输入如下字符串:“jdimov; cat /etc/passwd”字符串,Perl把分号作为元字符发送到内部命令Shell中,像下面这样:

cat /usr/stats/jdimov; cat /etc/passwd

入侵者将同时得到虚假的状态文件和密码文件,如果是比较有破坏性的,就可能仅仅发送“; rm rf /*”。

开始我们提到system()函数包含一个参数的列表,并且以命令的形式来执行列表的第一个元素,其他的元素作为参数来传递。所以,我们稍微改动脚本,就可以得到我们想要执行的命令结果。

      
system ("cat", "/usr/stats/$username");

即使我们单独指定每个参数到程序中,Shell也不会被调用。发送“;rm -rf /*”将不起任何作用,因为这个攻击性的字符串将仅仅作为一个文件名而被阻止。

这种方法比单参数的方法好得多,虽然它避免使用Shell,却仍然有潜在的缺陷。特别地,我们需要关心的是用户名的字段值能否被利用来攻击现行程序的弱点(以CAT为例),比如入侵者仍然可以利用我们重新写的编码版本去显示系统密码文件,那就是通过设置$username成字符串:“../../etc /passwd”。运行这个程序,会有很多地方出错。比如一些应用程序采用特殊的字符顺序作为执行Shell命令的请求,一个常见的问题就是一些UNIX MAIL工具的版本,当碰到“~”Esc控制序列时(在特别匹配的上下文中)会执行Shell命令,这样在特定的环境下,用户输入包含“~!rm -rf *”的字符将可能引起错误。

shells/program/perl/perl-exec.txt · 最后更改: 2010/09/01 10:18 (外部编辑)
[unknown link type]到顶部
GNU Free Documentation License 1.3
京ICP备05034962号 Debian Driven by DokuWiki get firefox browser Recent changes RSS feed Valid XHTML 1.0