原理
sql注入攻击的本质就是把用户输入的参数当做SQL语句来执行,Web应用程序对用户输入数据的合法性没有进行判断和过滤,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。
造成sql注入需要满足两个条件:
1.用户输入可控
2.输入参数被拼接成sql语句执行
危害
敏感信息泄露,获取数据权限,上传webshell,执行命令,篡改网页信息等
常见注入点
- GET/POST/PUT/DELETE参数
- http头注入(X-Forwarded-For)
- 文件名
- cookie注入
判断注入点
以下所有例子以mysql为主,且没有任何防护
注入点参数类型主要有数字型以及字符型,注入返回的数据不一定会反映在前端页面上,比如POST参数的注入,所以在寻找sql注入的时候需要时刻关注数据包。
数字型注入
当输入的参数为整型(int)时,则有可能存在数字型注入漏洞。
假设后台 SQL 语句为:
select * from users where id=1
① 输入参数:单引号 '
SQL 语句变为:
select * from users where id=1'
不符合语法,所以该语句会出错,导致脚本程序无法从数据库获取数据,从而使原来的页面出现异常。
② 输入 and 1 = 1
SQL语句变为:
select * from users where id=1 and 1 = 1
语法正确,执行正常,返回的数据与原始请求无差异。
③ 在数据库中输入 and 1 = 2
SQL 语句变为:
select * from users where id=1 and 1 = 2
语法正确,逻辑错误,因为 1 = 2 为永假,所以返回数据与原始请求有差异。
如果以上三个步骤全部满足,则程序就可能存在数字型 sql 注入漏洞。
字符型注入
同理,字符型注入即注入的参数为字符型(char),判断依据与数字型大致相同,不同的是,判断字符型注入需要先闭合''
。
例如,后台 SQL 语句为:
select * from users where id=''
利用'1'='1'
永真、'1'='2'
永假来判断,'
闭合前一个单引号,注释符--空格
或者--#
消除后一个单引号
select * from users where id='1' and '1' = '1' -- '
select * from users where id='1' and '1' = '2' -- '
两种语句均能正常执行,但永假语句不会返回数据或有明显差异
注入类型
判断完注入点参数类型即可用各种方法进行sql注入,字符型注意闭合即可。
常见sql注入类型有:联合注入、堆叠注入、报错注入、盲注(布尔和时间)、宽字节注入、偏移注入等。
联合注入
联合注入最主要的函数即union
。
在mysql中,union用于连接两个以上的 select 语句,并将结果组合到一个集合中。多个查询语句会删除重复的数据。
注入流程大致为:
确定字段数->确定回显位置->爆库->爆表->爆字段->爆数据
确定字段数
order by/group by函数
order by/group by
函数可以将查询数据排序后再返回数据
例如
select * from users where id=1 order by 1,2,3
表示查询结果根据第1、2、3字段排序输出
利用order by函数参数超出字段个数会报错的机制,可以判断出字段个数
例如表中只有3个字段
order by 4会报错,order by 3则不会,由此可以判断有3个字段
确定回显位置
union select
select * from users where id =11111111 union select 1,2,3
前一个查询语句select * from users where id =11111111
设置不存在的id参数会返回字段没有字段值;
后一个查询语句select 1,2,3
会返回字段值1,2,3没有字段;
两个查询语句的结果联合起来会返回正常字段,只不过字段值是1,2,3。
这时候如果页面上显示了’2’,说明’2’这个位置的参数是有回显的,这里就确定了回显位置,可以替换成别的参数,如database(),从而进行下一步爆破库、表、字段、数据的操作。
要注意一点的是,id参数要确保不存在数据库中,否则查询结果聚合后会对判断造成干扰。
爆库、表、字段、数据
mysql> =5.0版本提供了 infomation_schema库,这是一个信息数据库,它提供了访问数据库元数据的方式。
①information_schema.schemata表中字段schema_name存储了所有库的名称。
②information_schema.tables表中字段table_schema、table_name存储了所有库拥有的表名称。
③information_schema.columns表中字段table_schema、table_name、column_name存储了库、表、字段。
综上只要获取库(schema_name/table_schema)、表(table_name)、字段(column_name)就可以查询出数据。
tips:
①当回显只有一条时,为了提高效率,可以使用limit 0,x,或group_concat()函数
②当前库名不一定存在于schema_name中,可以使用database()查询当前数据库,类似的还有
version() MySQL 版本
user() 数据库用户名
database() 数据库名
@@datadir 数据库路径
@@version_compile_os 操作系统版本
select user,host from mysql.user 查询所有用户
select * from mysql.user where user=‘xxx’ 查询用户权限