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