首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>系统安全>文章内容
PHP漏洞中的战争
来源:http://www.luoyes.com/ 作者:落叶 发布时间:2011-06-14  
滥用include
1.漏洞原因:
Include是编写PHP网站中最常用的函数,并且支持相对路径。有很多PHP脚本直接把某输入变量作为Include的参数,造成任意引用脚本、绝对路径泄露等漏洞。看以下代码:
...

  1. $includepage=$_GET["includepage"];   
  2. include($includepage);  
...
很明显,我们只需要提交不同的Includepage变量就可以获得想要的页面。如果提交一个不存在的页面,就可以使PHP脚本发生错误而泄露实际绝对路径(这个问题的解决办法在下面的文章有说明)。
2.漏洞解决:
这个漏洞的解决很简单,就是先判断页面是否存在再进行Include。或者更严格地,使用数组对可Include的文件作出规定。看以下代码:

  1. $pagelist=array("test1.php","test2.php","test3.php"); //这里规定可进行include的文件   
  2. if(isset($_GET["includepage"])) //判断是否有$includepage   
  3. {   
  4. $includepage=$_GET["includepage"];   
  5. foreach($pagelist as $prepage)   
  6. {   
  7. if($includepage==$prepage//检查文件是否在允许列表中   
  8. {   
  9. include($prepage);   
  10. $checkfind=true;   
  11. break;   
  12. }   
  13. }   
  14. if($checkfind==true){ unset($checkfind); }   
  15. elsedie("无效引用页!"); }   
  16. }  

这样就可以很好地解决问题了。

小提示:有此问题的函数还有:require(),require_once(),include_once(),readfile()等,在编写的时候也要注意。

未对输入变量进行过滤
1.漏洞原因:
这个漏洞早在ASP中出现过,当时造成的注入漏洞不计其数。但由于PHP在当时的影响力较小,所以没有太多的人能够注意这点。对于PHP来说,这个漏洞的影响性比ASP更大,因为有比较多的PHP脚本使用到文本型数据库。当然也存在SQL语句的注入问题。举个比较经典的例子,首先是数据库的:

  1. $id=$_GET["id"];   
  2.   
  3. $query="SELECT * FROM my_table where id='".$id."'"//很经典的SQL注入漏洞   
  4. $result=mysql_query($query);  

这里很明显我们可以用注入来获得数据库的其它内容了。这里就不再详细叙述,和ASP注入一样的,大家可以看看以前的黑防。然后我们看文本数据库的问题:

  1. $text1=$_POST["text1"];   
  2. $text2=$_POST["text2"];   
  3. $text3=$_POST["text3"];   
  4.   
  5. $fd=fopen("test.php","a");   
  6. fwrite($fd,"\r\n$text1&line;$text2&line;$text3");   
  7. fclose($fd);  

文本的漏洞可以说是更加严重。倘若我们的提交的变量中插入一段很小的PHP代码,就可以另这个文本数据库test.php变成PHP后门。甚至插入上传代码,让我们可以上传一个完善的PHP后门。接着提升权限,服务器就是你的了。
2.漏洞解决:
这个漏洞的解决方法其实很简单,就是严格对全部提交的变量进行过滤。对一些敏感的字符进行替换。我们可以借助PHP提供的htmlspecialchars()函数来替换HTML的内容。这里给出一段例子:
//构造过滤函数

  1. function flt_tags($text)   
  2. {   
  3. $badwords=array("操你妈","fuck"); //词汇过滤列表   
  4. $text=rtrim($text);   
  5. foreach($badwords as $badword//这里进行词汇的过滤   
  6. {   
  7. if(stristr($text,$badword)==true){ die("错误:你提交的内容含有敏感字眼,请不要提交敏感内容。"); }   
  8. }   
  9. $text=htmlspecialchars($text); //HTML替换复制代码   
  10.   
  11. //这两行把回车替换为<br/>   
  12. $text=str_replace("\r","<br/>",$text);   
  13. $text=str_replace("\n","",$text);   
  14. $text=str_replace("&line;","│",$text); //文本数据库分隔符"&line;"替换为全角的"│"   
  15. $text=preg_replace("/\s{ 2 }/"," ",$text); //空格替换   
  16. $text=preg_replace("/\t/"," ",$text); //还是空格替换   
  17. if(get_magic_quotes_gpc()){ $text=stripslashes($text); } //如果magic_quotes开启,则进行\'的替换   
  18. return $text;   
  19. }  


  1. $text1=$_POST["text1"];   
  2. $text2=$_POST["text2"];   
  3. $text3=$_POST["text3"];   
  4.   
  5.   
  6. //过滤全部输入   
  7. $text1=flt_tags($text1);   
  8. $text2=flt_tags($text2);   
  9. $text3=flt_tags($text3);   
  10.   
  11. $fd=fopen("test.php","a");   
  12. fwrite($fd,"\r\n$text1&line;$text2&line;$text3");   
  13. fclose($fd);  

经过一番替换和过滤后,你就可以安全地把数据写入文本或数据库了。

管理员判断不完全
1.漏洞原因:
我们用PHP写脚本,通常要涉及管理员的权限问题。而一些脚本仅仅对管理员权限作出"是"判断,而往往忽略了"否"判断。在PHP配置文件中register_globals打开的情况下(4.2.0以后版本默认关闭,但有不少人为了方便而打开它,这是极度危险的行为),就会出现提交变量冒充管理员的情况。我们看一下的例子代码:

  1. $cookiesign="admincookiesign"//判断是否Admin的cookie变量   
  2. $adminsign=$_COOKIE["sign"]; //获取用户的cookie变量   
  3.   
  4. if($adminsign==$cookiesign)   
  5. {   
  6. $admin=true;   
  7. }   
  8.   
  9. if($admin){ echo "现在是管理员状态。"; }  

看上去好像很安全的样子,呵呵。现在我们假设PHP配置文件中register_globals为打开状态。我们提交这样一个地址“test.php?admin=true”,结果看到了吗?我们虽然没有正确的Cookie,但由于register_globals为打开状态,使得我们提交的admin变量自动注册为true。而且脚本缺少“否”判断,就使得我们顺利地通过admin=true取得管理员的权限了。这个问题存在于大部分网站和论坛当中。
2.漏洞解决:
解决这个问题,我们只需要在脚本中加入对管理员的“否”判断即可。我们仍然假设PHP配置文件中register_globals为打开状态。看一下的代码:

  1. $cookiesign="admincookiesign"//判断是否Admin的cookie变量   
  2. $adminsign=$_COOKIE["sign"]; //获取用户的cookie变量   
  3.   
  4. if($adminsign==$cookiesign)   
  5. {   
  6. $admin=true;   
  7. }   
  8. else  
  9. {   
  10. $admin=false;   
  11. }   
  12. if($admin){ echo "现在是管理员状态。"; }  

这样,就算攻击者在没有正确Cookie的情况下提交了admin=true的变量,脚本在以后的判断中也会把$admin设置为False。这样就解决了部分的问题。但由于$admin是变量,倘若在以后的其他脚本引用中出现了漏洞使得$admin被重新赋值就会引发新的危机。因此,我们应该使用常量来存放管理员权限的判定。使用Define()语句定义一个admin常量来记录管理员权限,在此以后若配重新赋值就会出错,达到保护的目的。看以下代码:

  1. $cookiesign="admincookiesign"//判断是否Admin的cookie变量   
  2. $adminsign=$_COOKIE["sign"]; //获取用户的cookie变量   
  3.   
  4. if($adminsign==$cookiesign)   
  5. {   
  6. define(admin,true);   
  7. }   
  8. else  
  9. {   
  10. define(admin,false);   
  11. }   
  12. if(admin){ echo "现在是管理员状态。"; }  

值得注意的是,我们使用了Define语句,所以在调用Admin常量时前面不要习惯性的加变量符号$,而应该使用Admin和!admin。

文本数据库暴露
1.漏洞原因:
前面已经说过,由于文本数据库具有很大的灵活性,不需要任何外部支持。加上PHP对文件的处理能力十分强,因此文本数据库在PHP脚本中的应用甚广。甚至有几个很好的论坛程序就是使用文本数据库的。但有得必有失,文本数据库的安全性也是比其他数据库要低的。
2.漏洞解决:
文本数据库作为一个普通的文件,它可以被下载,就像MDB一样。所以我们要用保护MDB的办法来保护文本数据库。把文本数据库的后缀名改为.PHP。并在数据库的第一行加入。这样文本数据库就会作为一个PHP文件,并且在第一行退出执行。也就是返回一个空页面,从而达到保护文本数据库的目的。

错误路径泄露
1.漏洞原因:
PHP遇到错误时,就会给出出错脚本的位置、行数和原因,例如:
Notice: Use of undefined constant test - assumed 'test' in D:\interpub\bigfly\test.php on line 3
有很多人说,这并没有什么大不了。但泄露了实际路径的后果是不堪设想的,对于某些入侵者,这个信息可是非常重要,而事实上现在有很多的服务器都存在这个问题。
有些网管干脆把PHP配置文件中的display_errors设置为Off来解决,但本人认为这个方法过于消极。有些时候,我们的确需要PHP返回错误的信息以便调试。而且在出错时也可能需要给用户一个交待,甚至导航到另一页面。
2.漏洞解决:
PHP从4.1.0开始提供了自定义错误处理句柄的功能函数set_error_handler(),但很少数脚本编写者知道。在众多的PHP论坛中,我只看见很少一部分对此情况进行了处理。set_error_handler的使用方法如下:
string set_error_handler ( callback error_handler [, int error_types])
现在我们就用自定义的错误处理把实际路径过滤掉。
//admin为管理员的身份判定,true为管理员。
//自定义的错误处理函数一定要有这4个输入变量$errno,$errstr,$errfile,$errline,否则无效。

  1. function my_error_handler($errno,$errstr,$errfile,$errline)   
  2. {   
  3. //如果不是管理员就过滤实际路径   
  4. if(!admin)   
  5. {   
  6. $errfile=str_replace(getcwd(),"",$errfile);   
  7. $errstr=str_replace(getcwd(),"",$errstr);   
  8. }   
  9.   
  10. switch($errno)   
  11. {   
  12. case E_ERROR:   
  13. echo "ERROR: [ID $errno] $errstr (Line: $errline of $errfile)<br/>\n";   
  14. echo "<b>程序已经停止运行,请联系管理员。</b>";   
  15. //遇到Error级错误时退出脚本   
  16. exit;   
  17. break;   
  18.   
  19. case E_WARNING:   
  20. echo "WARNING: [ID $errno] $errstr (Line: $errline of $errfile)<br/>\n";   
  21. break;   
  22. default:   
  23. //不显示Notice级的错误   
  24. break;   
  25. }   
  26. }   
  27. //把错误处理设置为my_error_handler函数   
  28. set_error_handler("my_error_handler");  

这样,就可以很好地解决安全和调试方便的矛盾了。而且你还可以花点心思,使错误提示更加美观以配合网站的风格。不过注意两点是:
(1)E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING是不会被这个句柄处理的,也就是会用最原始的方式显示出来。不过出现这些错误都是编译或PHP内核出错,在通常情况下不会发生。
(2)使用set_error_handler()后,error_reporting ()将会失效。也就是所有的错误(除上述的错误)都会交给自定义的函数处理。
其它有关于set_error_handler()的信息,大家可以参考PHP的官方手册。

POST漏洞
1.漏洞原因:
前面已经说过,依靠register_globals来注册变量是个不好的习惯。在一些留言本和论坛程序中,更要严格检查获得页面的方式和提交的时间间隔。以防止灌水式发帖和外部提交。我们看一下以下某留言本程序的代码:
...

  1. $text1=flt_tags($text1);   
  2. $text2=flt_tags($text2);   
  3. $text3=flt_tags($text3);   
  4.   
  5. $fd=fopen("data.php","a");   
  6. fwrite($fd,"\r\n$text1&line;$text2&line;$text3");   
  7. fclose($fd);  

...
很明显的,如果我们提交网址”post.php?text1=testhaha&text2=testhaha&text3=testhaha”。数据就会被正常写入文件中。此程序并没有检测变量的来源和浏览器获得页面的方式。如果我们向这个页面重复多次提交,就会起到洪水的作用。现在也有一些软件利用这个漏洞来在论坛或留言本上发广告,这是可耻的行为(我朋友的留言本就在1星期内被灌了10多页,无奈)。
2.漏洞解决:
在进行数据处理和保存前,首先判断浏览器的获得页面方式。使用$_SERVER["REQUEST_METHOD"]变量来获得浏览器的获得页面方式。检查其是否为”POST”。在脚本中使用session来记录用户是否通过正常途径(即填写提交内容的页面)来提交数据。或使用$_SERVER["HTTP_REFERER"]来检测,但不推荐这样做。因为部分浏览器没有设置REFERER,有部分防火墙也会屏蔽REFERER。另外,我们也要对提交内容检查,看数据库中是否有重复内容。以留言本为例,使用Session进行判定:
填写浏览内容的页面中,我们在最前端加上:
$_SESSION["allowgbookpost"]=time(); //登记填写时的时间
在接受留言数据并保存的页面中我们在进行数据处理前我们也用Session进行以下处理:

  1. if(strtoupper($_SERVER["REQUEST_METHOD"])!=”POST”){ die("错误:请勿在外部提交。"); } //检查页面获得方法是否为POST   
  2. if(!isset($_SESSION["allowgbookpost"]) or (time()-$_SESSION["allowgbookpost"] < 10)){ die("错误:请勿在外部提交。"); } //检查留言填写时的时间   
  3. if(isset($_SESSION["gbookposttime"])   
and (time()-$_SESSION["gbookposttime"] < 120)){ die("错误:两次提交留言的间隔不得少于 2 分钟。"); } //检查留言间隔

  1. unset($_SESSION["allowgbookpost"]); //注销allowgbookpost变量以防止一次进入填写页面多次进行提交   
  2. $_SESSION["gbookposttime"]=time(); //登记发送留言的时间,防止灌水或恶意攻击  
...
数据处理及保存
...
经过这样重重审查,你的程序就安全很多了。


 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·Windows 系统调用功能列表
·windows入侵提权-创建隐藏帐号(
· CC_STACKPROTECTOR防止内核stac
· Exploit The Linux Kernel NULL
·Linux内核安全研究之Stack Overf
·Kernel Locking 中文版
·Award BIOS Rootkit,universal
·IA32上Linux内核中断机制分析
·N种内核注入DLL的思路及实现
·PHP代码审计
·glibc 2.3.5 的一些新安全特性
·Struts2/XWork < 2.2.0 Remote C
  相关文章
·Shell 防止CC攻击
·PHP代码审计
·Linux下的入侵响应案例
· CC_STACKPROTECTOR防止内核stac
·Linux内核安全研究之Stack Overf
·*nux系统各版本的本地root密码破
·php扫马、内网查mysql数据库账号
·HOWTO: 在本地配置名字服务器并
·分析DLL劫持攻击
·windows堆溢出利用方式总结
·windows溢出保护原理与绕过方法
·php通用防注入程序
  推荐广告
CopyRight © 2002-2018 VFocuS.Net All Rights Reserved