zoukankan      html  css  js  c++  java
  • sql注入之报错注入原理解析

    sql注入报错注入原理详解 

    转载sql报错注入原理解析:https://blog.csdn.net/he_and/article/details/80455884

    前言

    我相信很多小伙伴在玩sql注入报错注入时都会有一个疑问,为什么这么写就会报错?曾经我去查询的时候,也没有找到满意的答案,时隔几个月终于找到搞清楚原理,特此记录,也希望后来的小伙伴能够少走弯路

    0x01

    我们先来看一看现象,我这里有一个users表,里面有五条数据:

    这里写图片描述

    然后用我们的报错语句查询一下:

    select count(*),(concat(floor(rand()*2),(select version())))x from users group by x
    

     

    这里写图片描述

    成功爆出了数据库的版本号。

    要理解这个错误产生的原因,我们首先要知道group by语句都做了什么。我们用一个studetn表来看一下:

    这里写图片描述

    现在我们通过年龄对这个表中的数据进行下分组:

    这里写图片描述

    形成了一个新的表是吧?你其实应该能够想到group by 语句的执行流程了吧?最开始我们看到的这张sage-count()表应该时空的,但是在group by语句执行过程中,一行一行的去扫描原

    始表的sage字段,如果sage在sage-count()不存在,那么就将他插入,并置count()置1,如果sage在sage-count()表中已经存在,那么就在原来的count(*)基础上加1,就这样直到扫描完

    整个表,就得到我们看到的这个表了。

    注:这里有特别重要的一点,group by后面的字段时虚拟表的主键,也就是说它是不能重复的,这是后面报错成功的关键点,其实前面的报错语句我们已经可以窥见点端倪了

    这里写图片描述

    ####0x02

    正如我前面所说的,报错的主要原因时虚拟表的主键重复了,那么我们就来看一下它到底是在哪里,什么时候重复的。这里rand()函数就登场了。

    首先我们先来了解rand()函数的用法:

    1.用来生成一个0~1的数

    2.还可以给rand函数传一个参数作为rand()的种子,然后rand函数会依据这个种子进行随机生成。

    那他们的区别是什么呢?我们来看一下,这两个语句的执行效果:

    这里写图片描述

    可以看到rand()生成的数据毫无规律,而rand(0)生成的数据则有规律可循,是:

    0110 0110

    注:如果你觉得数据不够,证明不了rand()的随机性,你可以自己多插入几条数据再查询试一下。

    0x03

    现在我们弄清楚了group by语句的工作流程,以及rand()与rand(0)的区别,那么接下来就是重点了,mysql官方说,在执行group by语句的时候,group by语句后面的字段会被运算两

    次。

    **第一次:**我们之前不是说了会把group by后面的字段值拿到虚拟表中去对比吗,在对比之前肯定要知道group by后面字段的值,所以第一次的运算就发生在这里。

    **第二次:**现在假设我们下一次扫描的字段的值没有在虚拟表中出现,也就是group by后面的字段的值在虚拟表中还不存在,那么我们就需要把它插入到虚拟表中,这里在插入时会进

    行第二次运算,由于rand函数存在一定的随机性,所以第二次运算的结果可能与第一次运算的结果不一致,但是这个运算的结果可能在虚拟表中已经存在了,那么这时的插入必然导致

    错误!

    所以我们现在通过一个例子来验证我们的理论,拿出我们最开始的例子:

    select count(*),(concat(floor(rand(0)*2),'@',(select version())))x from users group by x
    
    • 1

    声明:users表就是本文第一个表,表中有五条数据

    注意我这里用的是rand(0),不是rand(),

    rand(0)生成的有规律的序列:

    这里写图片描述

    我们跟着刚刚的思路走,最开始的虚拟表是空的,就像下面一样:

    count(*)x
       

    当我扫描原始表的第一项时,第一次计算,floor(rand(0)*2)是0,然后和数据库的版本号(假设就是5.7.19)拼接,到虚拟表里去寻找x有没有x的值是x@5.7.19的数据项,结果显然是没

    有,那么接下来就将它插入到上表中,但是还记得吗,在插入之前会进行第二次计算,这时x的值就变成了1@5.7.19,所以虚拟表变成了下面这样:

    count(*)x
    1 1@5.7.19

    现在扫描原始表的第二项,第一次计算x==’1@5.7.19‘,已经存在,不需要进行第二次计算,直接插入,得到下表:

    count(*)x
    2 1@5.7.19

    扫描原始表的第三项,第一次计算x==‘0@5.7.19’,虚拟表中找不到,那么进行第二次计算,这时x==‘1@5.7.19’,然后插入,但是插入的时候问题就发生了,虚拟表中已经存在

    1@5.7.19为主键的数据项了,插入失败,然后就报错了!

    上面是使用rand(0)的情况,rand(0)是比较稳定的,所以每次执行都可以报错,但是如果使用rand()的话,因为它生成的序列是随机的嘛,所以并不是每次执行都会报错,下面是我的测

    试结果:

    这里写图片描述

    执行了五次,报错两次,所以是看运气。

    总结

    总之,报错注入,rand(0),floor(),group by缺一不可

    小丑竟是我自己
  • 相关阅读:
    C++ 命名管道示例
    C#模块初始化注入
    【Oracle】存储过程写法小例子
    【Kettle】4、SQL SERVER到SQL SERVER数据转换抽取实例
    【Kettle】3、数据源连接配置
    【Kettle】2、文件夹与界面介绍
    【Kettle】1、简单介绍
    【Kettle】8、变量参数传递介绍
    【Oracle】DBMS_STATS.GATHER_TABLE_STATS详解
    【Oracle】DBMS_STATS.GATHER_SCHEMA_STATS详解
  • 原文地址:https://www.cnblogs.com/lspbk/p/14440832.html
Copyright © 2011-2022 走看看