企业如何搭建一个能满足自己需求的分布式检测系统?

一、概述

入侵检测系统(intrusion detection system,简称IDS),它通过实时监视系统和网络等,一旦发现异常情况就发出警告。根据信息来源可分为基于主机的IDS和基于网络的IDS。

本文主要是简单介绍基于主机型入侵检测系统(Host-based Intrusion Detection System,简称HIDS),提供一种快速实现的分布式HIDS的整体解决方案,对其中关键模块的关键技术进行探讨,提供了多种实现方案,并分析不同方案的优劣,为企业实现满足自己需求的HIDS系统提供参考。

当前,有许多开源的入侵检测系统,如OSSEC、WaZuh、Yulong-hids、AgentSmith-Hids等等,但是这些通用的HIDS不一定能满足企业自身需求。尤其是对于复杂的网络环境和个性化的功能定制需求,设计一套灵活可控的HIDS尤为重要。

入侵检测系统,按照功能主要有日志监控、文件完整性检测、Rootkit、安全基线、进程监控等。对于特征不明显的攻击行为,可以通过行为关联、机器学习等技术手段加强检测。

二、快速实现灵活的分布式入侵检测系统

为保证入侵检测系统的灵活性和稳定性,我们设计整体系统上时尽量保持KISS原则,各模块间尽量避免过于耦合,并且要满足以下几个特性:

o 易扩展、稳定性好

o 功能插件模块化,可以充分利用现有的开源项目

o 个性化规则下发(配置可以精细化单个服务器级别)

o灾备容错能力

o升级与回滚

2.1 系统总体架构

在很多时候,网络环境是非常复杂的,不同环境的网络是是无法直接连通的。这些完全或者不完全相互隔离的网络,我们定义为域。一个域可以是一个机房,也可以是多个机房,也可以是同一个机房不同的VLAN。因此,我们在设计系统的时候有必要考虑到整体系统的横向扩展能力。

为解决域的网络连通问题,有两种解决方案:一种是在域中边界部署单独的接入层集群,另外一种是在域上层部署接入层,然后打通各个域到接入层集群的网络。第一种方案,即在不同域中边界处单独部署接入集群,然后打通接入层集群到上层应用的网络。这种解决方案实现不难,但是会带来以下几个较大问题:

o 每个域都需要部署单独接入层集群,考虑到高可靠,至少得部署2台服务器,随着域的增加部署成本会急剧增加。

o 不同域的服务器数量不同的,有的多有的少,会导致接入集群的服务器资源浪费,无法充分有效利用起来。

o 后期开发维护成本很高,每次迭代升级时间成本都很高,极容易出问题。

因此,建议采用第二种方案,在域上层部署统一的接入层集群,然后直接打通各域到接入层网络,该方案可以有效降低部署成本和开发成本。

经过反复的设计推敲,去繁就简,分布式入侵检测系统的总体设计框架如下图1所示:  

图1:分布式入侵检测系统总体框架结构图

从上我们可以看出,整体系统架构从下到上主要分为客户端、接入层集群、分析集群、定时任务、WEB管控、离线分析六大模块。客户端是部署运行在业务服务器上,它会收集服务器上的信息上报到接入层,同时也接收执行从接入层下发的命令。接入层主要为客户端提供认证、授权、配置下发、上报数据预处理并推送到KAFKA队列等功能。而分析集群则是从KAFKA队列中消费客户端上报后预处理完之后的数据,利用预设的报警规则对数据进行模式识别,从而发现其中的异常入侵行为。定时任务应用主要是执行一些定时化的功能脚本,比如升级、告警推送、日志审计等等。离线分析模块主要是对数据进行离线分析,找出其中的攻击行为,提取攻击特征,以便更新收集规则和告警规则。

2.2 模块化的Agent

由于Agent是部署在业务的服务器上,因此有必要对其稳定性和资源占用进行严格的控制,并提供一种优雅的升级和回滚方式,避免对业务造成太大的侵扰。Agent的功能模块设计结构如下图2所示:  

图2:模块化的Agent结构图

在上图中,Agent采用了模块化的设计,主要分成守护模块、核心插件和普通插件。其中守护进程主要是监控守护常驻核心插件进程和常驻普通插件进程,对于不是常驻进程则不需要守护监控,比如升级回滚插件和脚本库。守护进程和接入层保持一个长TCP连接,主要是发送心跳保持在线状态。

o 核心插件:它是系统必须有的基本插件,主要包含通信交互和升级回滚两个插件。通信交互插件和接入层保持一个TCP长连接,实现发送收集数据和接收执行命令的功能。

o 普通插件:它是常规插件,可启用关闭,它可以和核心插件交互,主要是和通信交互插件通信。

如果从头到尾,重写一个Agent的所有功能,那么开发工作量势必会很高,所以有必要借鉴集成一些开源的数据收集模块或者入侵检测模块。经过慎重的对比分析,我们决定采用OSQuery作为数据采集的基础模块,并将其作为普通插件集成到Agent中,考虑到以后能够顺利升级,尽量少变更OSQuery原生代码,那么个性化的功能需求应该通过新的普通插件模块来实现。

插件化的模块设计,使得的Agent的升级和扩展都非常容易,尤其是插件的替换升级。比如,当OSQuery无法满足现有需求时,需要替换成另外一个组件易盾安全模块时,我们只需要将OSQuery这个普通插件卸载掉,开启易盾安全模块插件即可。

2.3 接入层

接入层是所有的Agent接入的入口,配有内网和外网地址。所有的Agent优先从内网接口接入,如果网络不通则使用外网接口接入,它主要功能如下:

o 提供注册、密钥更新功能。

o 配置信息下发更新配置。

o 提供接受用户指令等功能,并下发到对应的机器中执行,异步返回结果。

o 将收集上报的数据预处理后等推送到KAFKA队列中。

o 处理上传可疑文件,并保存到存储桶中

o 处理心跳、状态报告、处理结果等交互命令

2.4 分析集群

该集群主要是消费由Agent上报的信息,并基于Flink流式计算实现更为复杂的行为分析功能,检测潜在的异常攻击行为,其主要的功能如下:

o 聚合分析上报数据,应用告警规则检测潜在的入侵行为,产生不同级别的警告。

o 安全基线、合规规范

o 将Agent收集的信息(简单处理)推送到HBase中

2.5 通信协议

Agent和接入层的通信协议建议采用TLV格式:T表示数据类型,L表示数据的长度,V表示具体的数据。数据类型可以简单分成对象数据和文件数据两大类。

2.5.1 协议格式

其中数据分成以下对象数据和文件数据两大类:

o 对象数据:

对象数据主要包括控制命令、上报信息、下发配置等等,其格式定义如下:


o 文件数据文件数据主要是上传可疑的恶意文件,其格式定义如下: 

报文头部4个字节用于存放字符#^?#,用于数据错乱后的重新对齐,可以根据结合企业实际情况自定义字节数和魔法字符。

2.5.2 数据压缩

对交互数据的压缩能节省大量的网络带宽,尤其对于文本类的数据,压缩比很高。下图是各种库的压缩比对比图,可以看出snappy算法压缩和解压速率较好,可优先作为数据通信的压缩算法。 


2.6 收集规则

一般来说,若Agent收集的信息越多,那么检测出攻击的概率就越大。但是,对于部署了海量服务器的分布式入侵检测系统来说,收集上报太多信息,会消耗大量的网络资源和服务端计算资源;若告警检测全部在Agent来执行,势必会占用业务服务器的资源,严重可能引起业务中断。因此作为折中方案,有必要在Agent端对收集数据进行过滤,只上传比较重要或比较可疑的数据,主要的分析工作放在后端分析集群中。

2.6.1  收集方式

收集规则是在HIDS Agent端执行的规则集合,主要是用来指明收集信息的规则。依赖不同的实现方式,定义和执行方式会存在差异。根据信息收集的方式不同,分为以下四大类:

o 集成第三方模块收集

该收集方式是通过集成第三方模块收集获得的结果,比如OSQuery、OSSEC等。规则的形式依赖于第三方模块,比如OSQuery是SQL为主的json形式,OSSEC是为正则为主的xml形式。

o 脚本收集

该收集方式是通过周期性地执行脚本获得采集信息结果,主要形式为Shell脚本,python脚本、二进制类执行文件等。

o 预定义收集

该收集方式是指Agent自己内置的一些收集插件,不需要在上层配置具体的收集策略,比如Webshell检测等。我们在上层需要开关、或者配置简单的一些参数即可达到收集的目的,比如自动收集web目录,可以预内置代码逻辑,上层不需要配置具体的参数,只需要开启即可;而Webshell检测可能需要配置一些关键词或特征向量等简单的参数即可。

o 被动推送

该收集方式是第三方程序主动地收集数据,接入层被动接收数据的推送,它主要是满足当前的通用收集方式中无法满足特定业务个性化的信息采集需求。为此,业务需要自定义开发(通常需要提供HIDS SDK)相应的功能,然后将收集的信息推送到上层。该种接入方式的特点,是需要业务方接入SDK,或者按照数据的格式要求,自己收集数据,然后HIDS上层被动地接收业务数据的推送。

2.6.2 收集数据

收集的数据格式,是严重依赖于收集方式和收集规则。尤其是收集规则,自由度太大,会导致数据的非结构化以及嵌套化,这对于告警规则和告警规则解析引擎都会带来很大的复杂度,稳定性也将会受到影响,因此有必要对数据的字段做如下的规范限制:

o 字段具有原子性

每条记录的字段是一个原子的基本类型,是不可分解的,不能是一个复合的对象,比如Map,List等。

o 显式指明字段的类型

在收集规则中需要显式指明该规则收集的字段及其类型。接入层对于上报的数据,如果不符合要求的脏数据则直接扔掉。目前支持的字段类型限定为:INT,FLOAT, DOUBLE, STRING, DATE_TIME, CLOG, BLOG。

o 数据项(数据蔟)

由于限定字段都是原子性类型,不可再次拆解。为应对复杂的数据,保证其能降解为简单数据,必须要引入数据项的概念。数据项是同属一类的结构化数据集合,包含了多个不同基本类型字段。在mysql中可以简单理解为表,在HBase可以简单地理解为Family Column。

举个简单的例子:如果一个学校有2个班,一个班有10个人,一个班有20个人,那么如何将班级信息和班级人员收集上来,如果没有前面条件限定,我们可以直接用一个大的json上报所有的信息,那现在的做法将收集信息拆分两个数据项,分别是: 


2.6.3  采集数据类别

采集数据的大致分为基线相关数据、日志相关数据、攻击相关数据以及可疑文件四大类。有些分类信息是有交叉的,边界不一定非常棱角分明,比如用户命令和nc反弹shell本身就有交叉的关系,但是由于OSQuery本身就有这些检测功能的sql,所以可以单独拎出来,增加分析数据源。

其中,基线相关数据是指与主机合规加固相关的,主要包含(但不限于)以下数据: 

日志相关数据是指与用户或敏感程序的执行结果日志数据的,主要包含(但不限于)以下数据:

攻击相关数据是指用户或敏感程序的执行结果日志数据的,主要包含(但不限于)以下数据:

可疑文件是指可疑黑客攻击所使用文件或者被植入木马病毒的文件,用于确认文件是否恶意,主要包含(但不限于)以下数据:


2.7 告警规则

告警规则,是指针对收集上来的信息进行分析的触发条件,并指明触发规则后的告警信息。

2.7.1  分析数据对象

一个告警的信息,可能需要分析一个或者多个数据对象,才能得到有效的信息,更加规则应用的数据对象可分为如下三类。

o 单数据项

告警的触发规则应用到单数据项,编写容易,解释引擎也比较容易实现。

o 相同收集规则的多个数据项

告警的触发规则,由于空间维度不同,有一定的编写难度,且解释引擎实现有一定的难度。

o 不同收集规则多个数据项

由于不同收集规则的周期不同,时间和空间维度均不同,会导致规则异常复杂,实现解释引擎门槛要求较高。

2.7.2  触发规则

触发规则是指触发条件的表达式、或者一段具有判断分类的脚本。形式上来看,该规则可以是一个boolean型结果的表达式,也可以是一个具有判定分类的复杂脚本。

o 布尔表达式

通常的形式是一个简单表达式语句,执行之后得到一个确定的boolean值。比如,检测可疑文件名的布尔报警表达式语句如下。

比如,可疑文件名检测:

string.contains(filename,"muma") || 

string.contains(filename,"mimikatz")

表达式的语句形式,是取决于我们所使用的表达式解析库,目前可参考使用有Drools, IKExpression, Aviator和Groovy等。根据使用经验,JAVA推荐使用Aviator,性能最佳。

注意事项: 在选择规则解释引擎原型时,必须考虑是否支持自定义函数。 有些功能和业务联系比较紧密,不是常规函数所能解决的,比如黑白名单,业务逻辑等。

o 判定分类脚本

判定分类脚本,是指输出结果类型数量大于两种的情况,代码相对比较复杂。比如,我有个判定脚本,判定的结果有三种整数:0表示正常,1表示警告,2表示错误。该脚本编写有一定的难度,需要一定的经验,必须确保所有结果需投影在预设的结果集合中。

o 直接聚合产生告警信息

直接聚合产生告警信息的脚本比较复杂,是直接将规则匹配,告警信息产生全部糅合在一起的复杂脚本。编写较为复杂,不需要额外重量的引擎,全部的业务执行逻辑代码全部有业务方编写的代码完成,对业务方要求一定的编码能力。

o 频次阈值

对于一些可能会存在短暂异常信息,但是很快自动调整回来,并且不会造成业务影响的告警,我们可以启用频次阈值,尽量减少海量告警或误报。比如,网络上有大量的盲扫盲爆破,如果持续时间不超过10分钟,而且没有被爆破入侵成功的话,那么可以就可以忽略掉,因为每天发生这样的攻击次数实在是太多了。

o 告警信息

阈值规则触发条件满足时,则意味着一个异常事件被检测到了。告警信息,则是为了描述该异常事件,并通知给相应的运维和研发人员。

2.8 升级回滚

独立的更新回滚插件是核心插件,在需要升级是才启动,并不是常驻内存插件,主要完成对客户端可执行文件的更新,包括可能的失败回滚

2.9 离线分析,规则调优

离线分析是安全运维人员通过对离线数据,采用各种辅助工具和过程方法挖掘潜在的攻击行为,尤其是对于蜜罐中的攻击行为所收集的数据分析。通过数据分析,提炼出攻击特征,然后通过离线数据重放统计出召回率和正确率,并将最后总结出的收集规则和相对应的报警规则逐步应用到实践中。

三、戴明环式地安全运维开发

PDCA循环是美国质量管理专家休哈特博士首先提出的,由戴明采纳、宣传,获得普及,所以又称戴明环。全面质量管理的思想基础和方法依据就是PDCA循环。PDCA循环的含义是将质量管理分为四个阶段,即计划(Plan)、执行(Do)、检查(Check)、处理(Act)。在质量管理活动中,要求把各项工作按照作出计划、计划实施、检查实施效果,然后将成功的纳入标准,不成功的留待下一循环去解决。这一工作方法是质量管理的基本方法,同样是也适用于入侵检测系统的安全运维工作中。

3.1  计划(PLAN)

我们目标是通过搜集侵者者留下的蛛丝马迹,从而发现黑客的攻击行为,以便安全人员后续响应处理,重点是发现。那么就可以通过以下的方式来发现攻击并提取特征:

o 部署蜜罐,引诱黑客进行攻击,然后提取入侵者的攻击特征

o 通过之前发现的某一阶段的攻击,如下载恶意文件,然后提取出入侵者所有的攻击链路,分析所有的阶段特征。

o 用扫描爆破等入侵辅助工具,去扫描靶机,提取攻击特征

o 开源社区、线上线下沙龙交流等获得攻击特征

3.2  执行(Do)

在制定完计划之后,就按照上述计划实施策略。比如可以在外网不同区域部署多个蜜罐,收集入侵者所有的攻击行为数据,然后对数据进行分析,提取攻击特征。然后,再根据这些攻击特征编写数据收集规则和报警规则,下图是一个条收集规则和一条告警规则的示例:  

图3. 收集规则
 

图4. 报警规则

图5. 报警规则逻辑示例

在完成规则编写后,如果已相应的历史收集数据,则可通过离线分析模块,针对历史数据统计出规则的召回率和正确率,如果出现较大偏差,则继续调整规则。

3.3  检查(Check)

在规则确定之后,则逐步灰度发布到线上,然后把规则触发的报警信息标记哪些是正确的,哪些是误报的,明确效果。对应误报的告警,则需要分析找出原因所在。

 图6. 告警信息


图7. 告警邮件

3.4  处理(Act)

对于告警效果较好的规则则保留下来,正式发布到所有服务器上。对于存在较多误报的,则下线,然后进入下一个循环周期,不断地持续改进检测效果。

四、总结

入侵检测系统在实际的运维过程中,会碰到很多稀奇古怪的坑,但随着戴明环式地安全运维,稳定性和检测性能会持续改进。根据目前的运维经验,有以下两个坑需要稍微注意下:

文件完整性检测性能瓶颈:当启用了文件完整性检测的功能之后,如果监控的文件较多或者当系统正在更新的时候引起大量文件变化时,会占用很大的资源,如存储服务器(文件多),缓存用户可变化信息(Cookie等),系统更新(文件变化较多)等场景。

端口检测瓶颈:当启用了端口检测之后,就会实时检测端口的变化情况。但是当连接数较多时,每次枚举SOCKET时都会比较耗时,影响业务,如Web前端代理服务器,高并发的后端服务器等场景。

安全从来就不是一蹴而就的事情,需要充分地准备+长期的坚持+专业的安全技能+不断地精进,让入侵者无处遁形,切实保证企业的主机安全(文/易盾实验室)。

参考文献

  [1]. 入侵检测系统: https://baike.baidu.com/item/入侵检测系统/404710

 [2]. 祝亚楠: 《入侵检测系统面临的主要问题及其未来发展方向》

 [3]. 戴明环:https://baike.baidu.com/item/PDCA循环/5091521 

[4]. 企业安全建设之HIDS: https://www.freebuf.com/articles/es/194510.html 

[5]. 周健祥: 《基于神经网络入侵检测系统的研究与实现》

[6]. 使用osqueryd监控系统:https://blog.spoock.com/2018/11/27/usage-of-osqueryd/ [7]. Phoenix(SQL On HBase):https://www.cnblogs.com/funyoung/p/10249115.html