PHP格式化字符串漏洞
关键知识点
1 |
|
通过fuzz得知,在php的格式化字符串中,%后的一个字符(除了'%')会被当作字符类型,而被吃掉,单引号',斜杠\也不例外。
如果能提前将%' and 1=1#拼接入sql语句,若存在SQLi过滤,单引号会被转义成\'
1 | select * from user where username = '%\' and 1=1#'; |
然后这句sql语句如果继续进入格式化字符串,\会被%吃掉,'成功逃逸
1 |
|
还可以使用%1$吃掉后面的斜杠,而不引起报错
1 |
|
国外安全研究人员Anthony Ferrara给出了另一种此漏洞的利用方式
1 |
|
%c起到了类似chr()的效果,将数字39转化为',从而导致了sql注入。
0x02 漏洞原理
上述WordPress的SQLi的核心问题在于在sprintf中,'%s'的前一个'被吃掉了,这里利用了sprintf的padding功能

单引号后的一个字符会作为padding填充字符串。
此外,sprintf函数可以使用下面这种写法

**%后的数字代表第几个参数,$**后代表类型。
所以,payload%1$'%s'中的'%被视为使用%进行 padding,导致了'的逃逸。
0x03 php格式化字符串
但在测试过程中,还发现其他问题。php的sprintf或vsprintf函数对格式化的字符类型没做检查。
如下代码是可以执行的,显然php格式化字符串中并不存在%y类型,但php不会报错,也不会输出%y,而是输出为空
1 |
|
通过fuzz得知,在php的格式化字符串中,%后的一个字符(除了'%')会被当作字符类型,而被吃掉,单引号',斜杠\也不例外。
如果能提前将%' and 1=1#拼接入sql语句,若存在SQLi过滤,单引号会被转义成\'
1 | select * from user where username = '%\' and 1=1#'; |
然后这句sql语句如果继续进入格式化字符串,\会被%吃掉,'成功逃逸
1 |
|
不过这样容易遇到PHP Warning: sprintf(): Too few arguments的报错。
还可以使用%1$吃掉后面的斜杠,而不引起报错。
1 |
|
通过翻阅php的源码,在ext/standard/formatted_print.c的642行

可以发现php的sprintf是使用switch…case…实现,对于未知的类型default,php未做任何处理,直接跳过,所以导致了这个问题。
在高级php代码审核技术中的5.3.5中,提及过使用$order_sn=substr($_GET["order_sn"], 1)截断吃掉\或"。
之前也有过利用iconv转化字符编码,iconv('utf-8', 'gbk', $_GET['word'])因为utf-8和gbk的长度不同而吃掉\。
几者的问题同样出现在字符串的处理,可以导致'的转义失败或其他问题,可以想到其他字符串处理函数可能存在类似的问题,值得去继续发掘。
https://www.cnblogs.com/phpper/p/7546054.html


