PHP可变变量简介以及安全性问题分析

这个问题是源自于在平衡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
<?php
$a = 'hello';
$$a = 'world';
echo "$a ${$a}";
?>

最终输出是hello world。其实代码$a ${$a}就等价于$a $hello,所以这样就输出了hello world
那么应该如何理解PHP中可变变量呢?
我自己认为在${}中的代码就是可以执行的。在上面的例子中${$a},那么$a就是可以被解析的,最后解析为hello,所以最后就变为了$a $hello这样的代码。

变量执行

既然知道了在花括号内的代码是可以执行的,那么就可以尝试来进行利用。
如果代码写为:

1
2
3
<?php
"${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的版本中执行,如下:

  1. "${ phpinfo()}"; 第一个字符为空格)
  2. "${ phpinfo()}"; 第一个字符为tab
  3. "${/**/phpinfo()}"; 第一个字符为注释
  4. "${【回车】phpinfo()}"; 第一个字符为回车
  5. "${@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这种语言的灵活性,如果一旦使用不注意,说不定就会变成了一个安全问题了。

参考

http://www.yunsec.net/plus/view.php?aid=11894