sqli-labs 17-24笔记

Less 17

该关与之前的关卡有很大的区别

根据提示是密码重置

查看源码

 

 发现这里加入了一个过滤函数并对用户输入的用户名进行过滤 ' "等符号会被过滤,代表着不能在用户名处进行sql注入

 

 继续阅读源码,发现使用update语句对密码进行更新,这里不是查询语句,因此联合查询和两种盲注在这里都不能使用,这里使用的sql注入方法是报错注入

 报错注入有以下几种,extractvalue()注入,updatexml()报错注入和group_by()报错注入

 

1.extractvalue()报错注入

extractvalue()函数对xml文档进行查询

语法:extractvalue(目标xml文档,xml路径)

这里xml路径是操作的重点,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式

这是xml路径的正确格式,如果输入的格式不正确,就会报错并返回内容,返回的内容可以根据需要设置为需要查询的内容

在数据库中进行测试

如图

 

 在语法格式正确的情况下,即使查不到任何数据,也不会出现报错

 

 

 

 在语法格式错误的情乱下,即路径格式不是/xx/xx的情况下,会出现报错,且报错内容就是我们查询的内容

知道了原理后,回到这一关

之前通过源码得知闭合方式为单引号,构造注入语句如下

 


1' and (extractvalue(1,concat(0x5c,version(),0x5c)))#

进行测试

 

 一开始测试将用户名置为空,仅在密码处输入了sql注入的语句,发现失败,阅读源码

 

 发现这里如果不输入用户名或者说输入的用户名是错误的,则if中的条件测试不会通过,会执行else,也就是输出图片,所以要输入正确的用户名进行测试

 

 如图,成功爆出数据库的版本

继续进行测试

0x5c是十六进制的反斜杠

 爆表名


1' and (extractvalue(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c)))#

 

爆字段名


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

爆字段内容


1' and (extractvalue(1,concat(0x5c,(select password from (select password from users where username='admin1') As b) ,0x5c)))#

注意这里,因为mysql不支持在同一个表中同时进行查询和更新,所以我们要命名一个名为b的中间表进行数据查询

 

 

updatexml报错注入

UPDATEXML (XML_document, XPath_string, new_value)

updatexml与extractvalue注入类似,都是利用第二个参数的路径错误使得xpath语法报错,从而得到我们需要的数据

爆版本
1' and (updatexml(1,concat(0x5c,version(),0x5c),1))#

爆字段


123' and (updatexml(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name ='users'),0x5c),1))#

爆字段具体内容

1' and (updatexml(1,concat(0x5c,(select password from (select password from users where username='admin1') As b),0x5c),1))#

 

group by报错注入

count()的作用

 

 count()返回指定列的数量,如图,总共有十三列数据,所以返回13

group by用来对数据分组,相同的分为一组

 

 rand()产生一个0到1之间的随机数

 

 floor()用于向下取整

 

 

 

 rand()*2的范围是0-2

 

 取出一组不固定的0或1

 

 取出一组固定的0或1

group by的分组原理

select username,count(*) from username group by username;

 

 select username,count(*) from username group by "username";

 

 对于第一条语句,group by在分组时,会依次取出查询表中的纪录并创建一个临时表(key()和count),group by的对象就是该临时表的主键,如果临时表中存在该主键,则count +1 ,若不存在则将主键插入到临时表中,如group by username时,取第一个值admin,这会使在临时表中的主键key中查询admin这条数据,找到就count +1,没找到就插入admin,count +1

第二条语句,group by的对象是一个不变的字符串username,第一次插入username,后续只对count数进行递增

 

group by报错注入原理分析

select count(*) from information_schema.tables group by concat(database(),floor(rand(0)*2));

首先  floor(rand(0)*2)产生的随机数前6位一定是 0 1 1 0 1 1

concat(database(),floor(rand(0)*2))生成database()+"0"或database()+"1"的数列,而前六位的顺序一定是
database()+"0"
database()+"1"
database()+"1"
database()+"0"
database()+"1"
database()+"1"

报错的过程

建立临时表

取第一条纪录,执行执行concat(database(),floor(rand(0)*2)),结果为database+"0" 查询后发现这个主键不存在,于是准备执行插入,这是第一步的检测过程

继续执行第二步,执行插入,这个时候又会执行依次concat(database(),floor(rand(0)*2));则插入的结果时database+"1"而不是database+"0"

取第二条纪录,执行concat(database(),floor(rand(0)*2)) 结果为database+"1",查询后发现主键存在,count+1

取第三条纪录 执行concat(database(),floor(rand(0)*2)) 结果为database+"0",查询后发现主键不存在,则准备执行插入,这个时候会再次执行执行concat(database(),floor(rand(0)*2)),结果为database+"1",但主键中已经存在database+"1",所以爆出主键重复,同时带出数据库名

总之,group by报错注入利用的是第一步的查询结果与第二步的插入结果不同

回到less 17,这题也可以用group by报错注入完成

爆数据库

1' and (select count(*) from information_schema.tables group by concat(database(),0x5c,floor(rand(0)*2)))#

 

 爆出指定用户名的密码

1' and (select password from(select count(*) from information_schema.columns where table_schema=database() group by concat(0x7e,(select password from users where username='Dumb'),0x7e,floor(rand(0)*2)))As a)#

 

 与前两个类似,这里仍然需要构造一个中间表查询密码

Less 18

查看源码,发现对username和password字段都进行了过滤

 

 通过源码可知,当输入正确的用户名和密码时会打印user agent的相关信息,可以尝试利用这一点

 

 将ua输入1'报错

 

 从源码看出我们需要三个参数且要正确闭合

 

 页面正常回显说明参数正确且闭合正确

爆版本

 

 爆指定用户的密码

 

 (extractvalue(1,concat(0x5c,(select password from(select password from users where username='admin')As b),0x5c))))#

 

Less 19

与18类似,当输入正确的用户名和密码时,会显示referer字段

 

 尝试利用referer字段

 

 

 

 出现sql报错,同前关相同,构造

 

 回显正常,依旧是使用sql报错注入

 

Less 20

当输入的用户名和密码正确时,返回cookie内容

 

 在bp中尝试利用

 

 出现sql爆错,注释后消失

 

 因为不能利用查询语句,所以继续使用报错注入,这样在cookie字段显示的地方会输出我们需要的信息

爆数据库

 

 admin' and (extractvalue(1,concat(0x5c,database(),0x5c)))#

 

less 21

依旧是用户名和密码正确显示cookie,在bp中查看

发现uname被编码了

直接base64解码

 

 将上一关的注入代码base64编码

 

 这一关的闭合方式和上一关相比最后多个一个右括号,编码的时候要加上

Less 22

和21关类似,闭合方式为双引号没有括号

 

 

 

 

less 23

没有表单,重新回到了get请求

 

 单引号sql报错

注释

 

 

 

 注释符没有效果,猜测被过滤,阅读源码发现确实如此

 

 过滤了注释符意味着不能用order by猜测字段

这里用联合查询测试字段数

 

 

 

 说明字段数为3,最后的单引号是为了闭合id=-1处多出的单引号,id=-1为假,所以联合查询的结果可以返回

爆用户名和密码

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

 

 这里也可以不在最后放上单引号,可以使  ?id=-1' union select 1,(select group_concat(password,username) from users),3 or '1'='1

之前多出来的单引号会闭合最后的1,'1'='1'恒为真,我们只需要关注联合查询的结果即可

less 24

进入24关,发现有注册,登录,修改密码等功能,阅读源码

 

 

 

 

登录界面和注册界面都对输入进行了过滤,没有注入点

 

 在密码修改处可以看到使用的用户名是未经转义的用户名,这里是一个注入点

先注册一个名为admin'#的账户

 

 发现成功注册(从源码看到这里注册的账号中的'在网页端会被转义,但写到数据库中被还原了)

接着尝试更改admin'#的密码

 

 进入数据库查看,我们更改了admin用户的密码

 

 看源码分析 UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'

输入payload后

UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass'

真正生效的语句为

UPDATE users set password='111111' where username='admin'

所以成功修改了admin的密码

posted @ 2022-10-27 19:35  unknown27  阅读(241)  评论(0编辑  收藏  举报