DVWA笔记(7)--Blind SQL Injection(SQL盲注)

首先我们先了解一下SQL中基本的函数。

 

字符串截取类型:

 

mid(str,num1,num2)        从指定位数开始截取字符串

 

第一个参数是字符串

第二个参数是从字符串的第几位开始截取

第二个参数是一共截取几个字符

如:

select mid(‘hello’,1,2)               将会输出he。

 

substr(str,num1,num2)            同mid()

 

left(str,num)                      从左边开始截取指定位数的字符串

第一个参数是字符串

第二个参数是总共要截多少位           

如:

select substr(‘abc’,1,1)    将输出a

 

ascii码类型:

 

ORD()                        转换成ascii码

如:

select ORD(‘a’)                  将输出97

 

ascii()                         同ORD()

 

 

length()                     统计字符串长度

如:

select length(‘abcdefg’);          将输出7

 

字符串拼接:

concat(str1,str2,….)

将括号内的字符串全部拼接在一起

如:

select concat(‘h’,’e’,’l’ ’l’,’o’);              将输出 hello

 

version()  查看数据库版本

database() 查看数据库名

user() 查看当前用户

 

SQL盲注的核心就是通过判断返回了的显示结果是否一样,来判断SQL语句的执行。这要有足够的耐心。

我们来看dvwa的SQL盲注,就是一个很典型的盲注了。能查询到这个id就返回能,不能就不能,没有多余的报错。

 

LOW

首先,我们尝试使用SQL盲注的思维,来一点点破出该数据库版本。(这个每个人的版本可能是不一样的)

首先先确定该数据库版本的字符串长度。我们使用length()来统计。

 

‘ or length(version()) > 5#

 

这样子组合起来应该是

select xxx from xxx where id = ‘’ or length(version()) > 5 #

这样子,即使没有找到,但是因为or的存在,or的右边是认为数据库版本的字符串长度大于5.如果返回true。说明版本的字符串长度大于5,如果返回false。就是版本的字符串长度小于5。

返回了true。说明大于5。我们再一点点缩小范围。最后,我们得知数据库版本的长度为6

知道了长度,那么我们就可以再通过字符串截取,来一点点的获取每个字符的ascii码。构建语句如下:

‘ or ORD(mid(version(),1,1)) > 53 #

为什么是大于53呢。我们看看ascii码表,发现数字和点的ascii码就在46到57之间,那么53算是一个相对中间的位置。使用拆分查找,这样子效率会高一点。

我这里很巧,刚好第一个数是53。对照ascii码表发现53对应的是数字5。那么我的数据库版本的第一个字符就是5。

我们再来看接下来的字符。我们算第二个字符,构建语句如下。

‘ or ORD(mid(version(),2,1)) > 53 #

之后拆分查找,即二分查找。我获取到第二个ascii码为46。对照表发现是点。

这样我就发现了我的数据库版本的前两位是

5.

继续获取。这里就不赘述了。我最终获取到的是

5.5.62

我们到服务器中看看。确实如此。

只获取到数据库版本是不够的。我们尝试破出该数据库中所有数据表。

因为sql语句很长。我们现在控制台测试一下。

首先我们要获取到该数据库中表的数量。我们这样子来写。

所得的payload为

‘ or (select COUNT(*) from information_schema.tables where table_schema = database()) > 2 #

通过子查询,来构造我们的布尔。COUNT(*)统计出正在使用的数据库表的数量。整个子查询返回的就是一个数字。所以我们判断它是否大于2即可得到返回的布尔值。(能查询到就是true,查询为空就是false)

这样子我们就知道了该数据表的数量为2。

接着我们破第一个数据库的名字的长度。构建payload

‘ or (select LENGTH(table_name) from information_schema.tables where table_schema = database() limit 0,1) > 5 #

或者可以这样写,一样的:

' or length((select table_name from information_schema.tables where table_schema = database() limit 0,1)) > 5 # ;

多次实验。发现第一个表名长度为9

知道了表的长度,那么我们可以逐个破出来了。我们构建payload

' or ORD(MID( (select table_name from information_schema.tables where table_schema = database() limit 0,1),1,1) ) > 94 #

这里是在MID中嵌一个select子句,通过mid截取字符,接着判断ascii码。主要就是不要弄混了括号就好。最好先在控制台测试一下,避免出错。

我最终得到的第一个字符的ascii码是103。即小写字母 “g”。之后的就把MID截取改一下即可。慢慢破。这里由于篇幅原因就不写了。知道思路即可。

 

这就是基于布尔的盲注的基本思路了。其实和普通的sql注入一样。只是回显的结果有点麻烦而已。其他级别的代码我也看了一下。和普通的sql注入一样的注,只不过换成布尔盲注的形式即可。所以接下来就不详细说了。下面说的是盲注的扩展。

 

我们来分一分盲注的类型:

  1. 基于布尔的盲注。就是我们在dvwa上弄的。主要就是根据回显结果判断是否执行成功。
  2. 基于报错的盲注。这个就是精心构建一个报错的SQL语句。让我们想拿到的内容在报错中出现。
  3. 延时注入。

 

  1. 布尔盲注:

上面在dvwa上进行的布尔盲注,就是最基础的布尔盲注了。他先通过字符串截取,然后逐个判断字符的ascii码值,之后再去表中对照来一个个的获取到我们想要的回显结构。接下来要说的这两个是使用正则表达式和通过模糊查询来进行布尔盲注。

  1. regexp

如果熟悉正则,那么玩这个正则注入就得心应手了。我们先来看看regexp的用法。

可以看到。regexp后面就跟一个正则语句即可。那么我们可以构造这样子的一个语句:

‘ or user() regexp ‘^root’ #

这样子就可以知道当前用户是不是root了。

  1. like

我们知道。like是mysql用于模糊查询的一个语句。用法如下:

select xxx from xxx where username like ‘%xiaopan%’;

这个就会匹配到所有包含xiaopan的数据。那么我们注入的时候如果这样子构造一个语句

‘ or user() like ‘%root%’ #

这样子就能知道当前的mysql用户是否是root了。

  1. 报错注入:

在没有回显内容时,但是页面允许输出报错信息时。可以通过报错注入获取我们需要的信息。就是通过构建一包含子查询的能够让mysql报错的一个语句。报错输出是位置显示在我们构建的子查询中。这样子我们就能通过这个子查询获取我们所需要的信息。

  1. bug注入

count ,rand,group by 同时使用,前当前数据表中数据超过三行。就可以触发报错。

这个是一个bug。我们试试不同时使用的时候。是什么情况。

虽然我们能够得到我们想要的返回结果。可是。。这个不是报错显示的,这是正常显示的。要是我们是盲注的话,也就是只返回了执行成功之类的话。我们也不知道我们要的东西长什么样。

所以我们要构建这样的语句:

select concat(floor(rand(0))*2),(select user()) xx,count(1) from users group by xx;

这里rand(0)一定要乘一个非1的数字才能触发报错。不然又是可以正常显示的。

这就是那三者的融合技。至于这个为什么就可以报错。百度了一下。是因为mysql的bug:

引用大佬的话  https://blog.csdn.net/jpygx123/article/details/84191704:

”当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。”

“group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表中的数据;如果该key不存在于临时表中,则在临时表中插入key所在行的数据。group by floor(random(0)*2)出错的原因是key是个随机数,检测临时表中key是否存在时计算了一下floor(random(0)*2)可能为0,如果此时临时表只有key为1的行不存在key为0的行,那么数据库要将该条记录插入临时表,由于是随机数,插时又要计算一下随机值,此时 floor(random(0)*2)结果可能为1,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值不一致,导致插入时与原本已存在的产生冲突的错误。”

还有一个大佬的实验:https://www.cnblogs.com/xdans/p/5412468.html

Em。。。看不懂的话就先记这把。

1、extractvalue() 报错

这个函数是用于xml的操作的。用于提取xml里的字符。第二个参数中如果不是xml格式的。就会报错。并且将非xml格式的数据显示出来。就能成功报错查看信息。

 

select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

 

这里0x7e的意思是上波浪号~。这样就可以显示出我们要的数据。其实换成其他的也行。比如0x02。就是用来隔开便于显示的。

 

2、 updatexml() 报错

和extractvalue()原理一样。这个函数用于更新xml文档。只不过他要三个参数。

 

select * from users where user_id = 1 and (updatexml('test',concat(0x02,(select database())),'test'));

3、其他的诸如 multipolygon polygon之类的我在本机上试了。也许因为数据库版本5.7的原因无法成功报错。就没有继续写了。

报错如下:

并没有获取到有用信息。

 

补录:

今天玩CTF的时候。有道题的mysql版本是5.5。上面能用的报错注入函数都禁用了。正准备放弃的时候。试用了一下multipolygon这个函数。万万没想到。居然ok了。

multipolygon用法

select * from information_schema.tables where table_name='1' and multipolygon((select * from(select * from(select user())a)b))

 

爆错输出当前所有数据库

 

我们报错输出当前的数据库所有的数据表

 

这是一个ctf题目。我们看到我们要取的flag的数据表的。但是我们还不知道里面的字段是什么。所以我们先获取字段。

 

获取到flag存放的字段就是叫做flag。那么我们就可以直接select出来了

 

 

  1. 延时注入:

就是当注入点不回显东西的时候,那我们怎么判断注入的结果呢。这样就引来了延时注入。

我们先来看两个语句

  1. if(条件,动作1,动作2)

这个是sql语句中的if。当条件为true时。执行动作1,当条件为false时,执行动作2。

  1. sleep(second)

这个就类似于各大编程语言的sleep。作用就是在语句执行过程中。停顿second的时间。如:

sleep(5)      就是停顿5秒。

可以看到,是每查询一个数据就停顿相应的秒数。

 

那么,延时注入就是结合这两个。因为我们无法从页面中获取任何回显。所以我们就自己构造我们的回显。我们可以得到如下语句:

if(length(user()) > 5,sleep(2),1)

这个意思是。如果用户名长度大于5,那么就停顿2秒再执行。如果不是,就什么都不执行。这样子我们就可以根据页面的相应速度来判断是否执行成功了。

猜你喜欢

转载自blog.csdn.net/xiaopan233/article/details/88093980