这个问题是源自于在平衡php4fun-wirteup文章的第二题的答案,由于当时的我最后的POC写的是1
http://localhost/php4fun/2/index.php?str={{phpinfo()}}
始终无法复现,后来去研究了一下,发现这是一个PHP的可变变量的问题。
PHP可变变量
上述的这种情况在php中被称之为可变变量,也就是说像<?php "${@phpinfo()}"; ?>
这种写法中,phpinfo()
是可以被执行的。下面的代码是php中的官方代码,对php中的可变变量的解释。1
2
3
4
5
$a = 'hello';
$$a = 'world';
echo "$a ${$a}";
最终输出是hello world
。其实代码$a ${$a}
就等价于$a $hello
,所以这样就输出了hello world
。
那么应该如何理解PHP中可变变量呢?
我自己认为在${}
中的代码就是可以执行的。在上面的例子中${$a}
,那么$a
就是可以被解析的,最后解析为hello,所以最后就变为了$a $hello
这样的代码。
变量执行
既然知道了在花括号内的代码是可以执行的,那么就可以尝试来进行利用。
如果代码写为:1
2
3
"${phpinfo()}";
可以发现这样是无法执行的,这个特性和PHP的版本有关系,我通过phpstudy测试发现,在5.2.17、5.3.29、5.4.45是不行的,但实际在5.5.38、5.6.27、7.0.12的版本中是可行的。所以我猜测这个特性在php5.4.45以下的版本中都是无法执行的,但是在之后的版本都是可行。
虽然如此,但是有些写法可以在任何php的版本中执行,如下:
"${ phpinfo()}";
第一个字符为空格)"${ phpinfo()}";
第一个字符为tab"${/**/phpinfo()}";
第一个字符为注释"${【回车】phpinfo()}";
第一个字符为回车"${@phpinfo()}";
第一个字符为@
原理是什么呢?空格,tab,注释,回车是各种语法分析引擎中常见的分割字符,@是PHP语法的一个特殊的容错符号,所以可变变量内的花括号有这么一个规则,需要判断花括号内的内容是否为真正的代码,条件即是文本的第一个字符串是否为PHP语法解析引擎的分割字符和特殊的语法符号!
这样就解释了P牛的代码<?php "${@phpinfo()}"; ?>
为什么可以执行了。
可变变量在webshell中的利用
可变函数的思想
将特征方法(assert,eval)拆开。1
2
3$a="ss";
$var="a".$a."ert"
$var("phpinfo();");
总结
在P牛的文章Python 格式化字符串漏洞(Django为例),P牛也提到了这种用法<?php "${@phpinfo()}"; ?>
,当然P牛的这篇文章分析的Python的格式化的问题。
本文主要讲解在可变变量的含义以及可变变量在webshell中的利用方式,利用PHP的可变变量的特性,可以是webshell有很好的隐藏效果。其实除了在webshell中的利用之外,这种方式应该还有更多的其他的用法。对于PHP的这种动态语言,有很多千奇百怪的特性。这种特性在Python中并没有。正是由于PHP有了这么多奇怪的特性,使得PHP在编写webshell的时候可以非常的灵活。在语义保持不变的情况下,可以变换语法来实现webshell,非常的灵活,这也是目前的基于文件内容来检测webshell的最大障碍。
PHP这种语言的灵活性,如果一旦使用不注意,说不定就会变成了一个安全问题了。