格式化字符串引发的安全问题
PHP部分
1).vsprintf
和sprintf
导致的单引号逃逸问题
两个函数都是用来格式化字符串的。
函数区别
区别在于vsprintf
接收的参数是数组,sprintf
接收的参数是字符串。
1 | <?php |
返回结果都是一样的:
1 | Tom is a boy. |
基本用法
1 | <?php |
注:最后面的s代表的是参数类型(s表示是字符串,还可以是其他类型,比如d,c等)
执行结果:
1 | | Tom| is a boy. |
前3个输出比较好理解,是基本的填充功能。
比较有意思的是后两个:
1 | echo sprintf("|%'#5s| is a %s.\r\n", $name, $sex); //表示用\#填充到5个字符 |
%'#5s
表示用#
把字符串填充到5个字符。
PHP规定如果用空格和0以外的字符进行填充的话,必须用单引号在前面”转义”。
%1$'#5s
表示用后面的第1个参数($name
)进行替换,并且用#
填充到5个字符。
%2$'#5s
表示用后面的第2个参数($sex
)进行替换,并且用#
填充到5个字符。
以此类推。
单引号逃逸方式一
1 | <?php |
我们提交参数name=Tom%1$c-- sdf&sex=39
,$sub_sql
就变成了:
$sub_sql = " AND name = 'Tom%1$c-- sdf'";
格式化之前的$sql
就成了:
1 | SELECT * FROM ADMIN WHERE sex = '39' AND name = 'Tom%1$c-- sdf'" |
sprintf
格式化之后, %c
为格式化为字符格式,39
是单引号的ASCII值,直接被格式化为单引号,触发逃逸。
单引号逃逸方式二
这里是另外一个技巧。
前面测试如果利用非空格或者0进行填充,需要在填充字符前添加单引号,比如:
1 | <?php |
如果不添加这个单引号,测试一下:
1 | <?php |
得到结果:
1 | |5s| is a boy. |5s| is a ##boy. |
对比发现,%#5s
和%1$#5s
都消失了,5s被当做字符串输出。也就是说,%#5s
和%1$#5s
都被“吃掉”了。
也就是说,如果不加单引号进行填充,任何填充字符都会被前面的%
吃掉,包括\
。
如下漏洞代码:
1 | <?php |
这段代码如果我们直接提交name=Tom'&sex=Boy
,因为addslashes()
的缘故,SQL会变成:
1 | SELECT * FROM ADMIN WHERE sex = 'Boy' AND name = 'Tom\'' |
单引号无法逃逸。
但是我们提交name=Tom%'&sex=Boy
,这样经过addslashes()
之后Tom%\'
,然后%吃掉\
,导致单引号逃逸成功。
因为参数个数的问题,如果上面这个会提示:
1 | Warning: sprintf(): Too few arguments |
没关系,我们可以用%1$
这种方式指定所有占位符都读取第一个参数。
我们提交 name=Tom%1$'-- sdf&sex=Boy
,触发逃逸,单引号成功闭合。
PYTHON部分
参考:
https://paper.seebug.org/386/
https://github.com/80vul/pasc2at
https://www.leavesongs.com/PENETRATION/python-string-format-vulnerability.html
- 本文链接:http://l4yn3.github.io/2019/04/01/格式化字符串引发的安全问题/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!