由于 Web 应用程序的复杂性和重要性, 导致其成为网络攻击的主要目标之一。攻击者在入侵一个网站后, 通常会植入一个 Webshell, 来持久化控制网站。但随着攻防双方的博弈, 各种检测技术、终端安全产品被广泛应用, 使得传统的以文件形式驻留的 Webshell 越来越容易被检测到, 内存型 Webshell 成为新的趋势。本文面向 Java 应用程序, 总结内存型 Webshell 的特征和原理, 构建内存型Webshell 威胁模型, 定义了高对抗内存型 Webshell, 并提出一种基于RASP(Runtime application self-protection, 运行时应用程序自我保护)的动静态结合的高对抗内存型 Webshell 检测技术。实验表明, 与其他检测工具相比, 本文方法检测内存型 Webshell 效果最佳, 准确率为 96.45%, 性能消耗为 7.74%,具有可行性, 并且根据检测结果可以准确定位到内存型Webshell 的位置。
目录
4 高对抗内存型 Webshell 检测技术
4.2 动态特征检测算法
4.3 静态特征检测算法
4.4 告警处置
4 高对抗内存型 Webshell 检测技术
本节针对内存型 Webshell 的原理和特征, 提出一种基于 RASP 技术的动静态结合的高对抗内存型Webshell 检测方法, 其总体流程如图 5 所示。首先对应用程序进行监测, 在有用户请求的情况下,利用 RASP 监测技术获取监测函数的上下文信息。根据获取的行为特征, 结合磁盘是否存在文件、黑白名单过滤、数据流分析技术进行动态特征检测; 获取JVM中所有加载的类, 通过特征过滤筛选出高危类, 利用深度网络ResNet50分类模型进行静态文本特征检测。动态检测中监测到的非 JS 场景的新增组件类及 defineClass 函数输入到静态检测中, 动静态检测方法结合判断请求或系统内部是否存在高对抗内存型 Webshell。最后根据判定结果进行处置并发出告警信息。
4.1 RASP 监测技术
针对内存型 Webshell, RASP 主要监测两类函数, 一类是与输入源中的组件相关的注册组件类函数, 另一类是为达到特权状态所请求的函数,称为特权类函数。结合内存型 Webshell 的原理可知,前者主要关注其在注入过程中的函数请求, 后者主要关注注入成功后访问 Webshell 时的函数请求。
(1) 注册组件类函数
内存型 Webshell 的输入源中包含组件, 该类型的 Webshell 需要依靠组件将 Webshell 驻留在运行内存中, 所以会通过调用注册组件类的方法以达到目的, 而其他的正常行为或攻击行为通常不会通过该方式添加一个组件。注册组件类函数通过调用某个类可以直接在内存中生成一个新的组件, 不同类型的组件注册的方式不同, 调用的函数也不同。以张金莉 等: 面向 Java 的高对抗内存型Webshell 检测技术 69Tomcat添加Filter为例, 在内存型Webshell的实际环境中, Filter 并不会按照在 web.xml 中注册的方式添加, 而是通过反射机制进行动态注册。
注册 Tomcat 的 Filter 组件关键步骤是:
1) 通过反射获取 ServletContext 对象。
2) 实例化一个包含 Payload 的恶意 Filter 对象。
3) 利用FilterDef对Filter进行封装, 定义其名称和类名等, 将 FilterDef 添加到 FilterDefs 中。
4) 创建 FilterMap, 设置 Filter 和拦截的 URL 的映射关系, 并将该 FilterMap 作为第一个过滤器添加到 FilterMaps 中。
5) 创建 FilterConfig, 封装 FilterDef 对象, 并添加到 FilterConfigs 中。
6) 最后把 FilterDefs、FilterMaps、FilterConfigs注入到 StandardContex 即可。
整个过程中与Filter组件注册紧密相关的函数在第 3 步 中 , 如 图 6 所 示 , 包 括 setFilter() 和setFilterClass()方法, 这两个方法可以通过参数信息分别获取实例化的 Filter 对象和 Filter 的类名。
(2) 特权类函数
内存型 Webshell 与普通的 Webshell 相似, 最终目标都是持久化控制目标服务器, 达到一定的特权
状态。该特权状态包括任意系统命令执行、操作服务器文件、对数据库操作等恶意行为状态, 涉及的特权类函数包括命令执行函数、文件操作函数、数据库操作函数、编解码函数等敏感操作函数。通过 Hook相关函数, 可以检测和拦截 Webshell 对系统造成危害行为的操作。
除此之外, 针对内存型 Webshell 的特性, 还包括一类特殊函数。例如, 一般情况下, 内存型 Webshell 会随着组件的生命周期在 Web 服务器关闭或重启后, 也会随之销毁, 但是攻击者针对这一缺陷, 通过添加 JVM 的关闭钩子 ShutdownHook, 使得服务器在关闭时启动一个线程将内存型 Webshell落地磁盘, 当服务器重启后, 磁盘文件自动执行再删除, 内存型 Webshell 就可以长期驻留在运行内存中。在 startUpClass 中植入内存型 Webshell, 同样可以做到持久化的效果。还有些内存型 Webshell 为了更隐蔽地达到特权状态, 在新增的组件类或Agent方式修改的类中通过 defineClass 方法加载字节码文件产生一个新的类, 在新的类中注入恶意代码, 使得检测难度增加, 攻击更加隐蔽。
4.2 动态特征检测算法
根据内存型 Webshell 的原理, 一般会注册一个新的组件到内存中, 达到磁盘上无文件的目的。所以
可以根据新增组件和磁盘无文件两个特性判断是否为内存型 Webshell。因为正常的服务请求或者其他的攻击行为一般不会新增一个 Java Web 组件, 这是内存型 Webshell 的独有特性。可以根据 RASP 监测的注册组件类函数, 获取上下文信息, 从拦截的函数参数中获取新增的组件类。然后检测磁盘上是否存在对应类文件, 根据触发器的不同场景, 分 3 种情况讨论。
1)如果新增组件是通过字节码注入或远程方法调用实现, 并且磁盘上没有对应类文件, 则判定为
内存型 Webshell。
2)新增组件是通过字节码注入或远程方法调用实现, 但是, 有的攻击者为了规避磁盘无 文 件 这 一 特 征 , 故 意 将 字 节 码 文 件 写 入 到ClassPath 路径下, 并在 web.xml 文件中配置相关信息, 来绕过检测。针对这种情况, 如果检查到磁盘上存在新增的组件类, 不能确定为非内存型 Webshell,需要针对存在的新增类, 结合静态特征检测进一步判断, 将在下一节讨论。
3)新增组件是通过 JSP 文件实现, 则 JSP 文件执行时, 即新增组件注册时, 一定会产生磁盘类文件, 动态检测是在新增组件注册时进行检测的, 无需检查磁盘是否有文件, 直接判定为内存Webshell。
内存型 Webshell 在注入到目标应用程序之后,也跟正常 Webshell 一样, 最终会触发特权类函数来操控目标服务器, 例如操作文件、执行系统命令等行为或执行关闭钩子函数使 Webshell 更加持久化。应用程序内部的特权类函数调用, 通过设置黑白名单进一步过滤敏感行为, 如文件类特权函数设置上传文件后缀白名单, Webshell 常访问的路径范围设置黑名单等。即使是敏感的行为操作也可能是其他攻击类型, 根据内存型Webshell的特性, 设计动态特征检测算法, 如图 7 所示。
动态特征检测算法的伪代码如算法 1 所示。输入包括请求上下文、注册组件类函数的上下文、特
权类函数的上下文, 以及用于过滤敏感行为的黑白名单列表。输出为算法的判定结果。
算法 1. 动态特征检测算法.
输入: 请求上下文 reqContext、注册组件类函数上下文 compContext、特权类函数上下文 priContext、黑白名单列表 list
输出: 判定结果 result
IF !Empty(reqContext)
IF !Empty(compContext)
获取新增组件类 newCompClass
IF ClassLoader.getResource (newComp-
Class) == jsp 文件
RETURN true, result(Webshell)
ELSE IF ClassLoader.getResource (new-
CompClass)没有获取到类文件
RETURN true, result(Webshell)
ELSE
newCompClass 标记为 riskClasses
静态特征检测(riskClasses)
END IF
END IF
IF !Empty(priContext)
Get priContext.参数
Get priContext.调用栈
IF priContext.参数 MATCH list
IF priContext.调用栈 Contains new-
CompClass
RETURN true, result(Webshell)
ELSE
RETURN true, result(Other)
END IF
ELSE
RETURN false
END IF
END IF
END IF
4.3 静态特征检测算法
由于 JVM 中加载的类很多, 如果全部获取并检测则会影响检测效率, 因此根据内存型 Webshell 的
原理和特性, 我们尝试在正常请求的完整函数调用栈上使用javassist框架对函数依次进行Agent字节码修改, 修改成功的类可能被攻击者利用为内存型Webshell 攻击, 经过多轮请求测试, 提取了 JVM 中五种类型的高危类, 包括类名、父类、接口、ClassLoader、注解。
(1) 类名, 包含容易被修改的类的名字以及Agent 类 Webshell 常使用的特殊的类名字。
2) 父类, Webshell 如果是自定义的类, 则需要继承父类以重写相应的方法执行恶意功能。
(3) 接口, 内存型Webshell为了最终能够执行恶意功能, 会实现某些接口。
(4) ClassLoader, 内 存 型 Webshell 使 用 的ClassLoader 一般与反序列化、代码执行等漏洞相关,与正常的类加载器有所区别
(5) 注解, 内存型Webshell实现时会使用一些注解, 例如 Spring 的 Controller 类型的 Webshell 通常使用 Spring 的注解来声明。
表 5 列举了 JVM 中部分内存型 Webshell 相关的高危类。
静态特征检测算法如图 8 所示。首先获取JVM 中加载的所有类, 然后根据特征筛选出 5 种类型的高危类, 对高危类进一步检测。除此之外,高危类还包括动态特征检测中, 通过请求新增的组件类, 并且该组件类是通过非 JSP 注册的在磁盘上存在 class 文件的情况, 该新增的组件类也被归为高危类。
另外动态特征检测过程中, 如果触发的特权类函数是defineClass函数, 则根据RASP监测获取其上下文信息, 根据其加载的字节码文件进一步检测。
经
过不断的学习, 深度模型具备了对 Webshell 样本图像和白样本图像的分类能力。保存测试结果最优的那一轮次的模型用于最终任务的检测。在实际检测中, 首先将要检测的“.class”文件或字节码文件反编译为“.java”文件, 然后将“.java”文件转化成的灰度图输入深度检测模型中, 模型输出是 Webshell 样本或白样本。
静态特征检测算法的伪代码如算法 2 所示。以JVM 中加载的所有类作为输入, 以筛选出高危类,
同时高危类中也包含动态检测过程中输出的高危类;输入还包括 defineClass 函数中的字节码文件。最后输出为算法的判断结果。
算法 2. 静态特征检测算法.
输入: JVM 加载类 classes、defineClass 的字节码文件 bytecode
输出: 判定结果 result
Get JVM all classes
FOR class in classes
IF class MATCH 特征过滤
class 标记为高危类 riskClasses
END IF
END FOR
FOR class in riskClasses
模型检测 f()
RETURN result
EDN FOR
IF !Empty(bytecode)
模型检测 f()
RETURN result
END IF
4.4 告警处置
告警处置用于对内存型 Webshell 检测结果进一步处理。针对有请求的动态检测结果, 在 RASP 监测模块已经对请求类函数进行了监测, 如果检测结果为正常, 则返回原始响应页面, 如果检测结果为内存型 Webshell 或者其他高危行为, 则对请求拦截并返回一个自定义的页面, 同时使用 SimpleEmail [36] 发送邮件告警的通知。针对 JVM 加载类的静态检测结果, 直接通过邮件方式发出告警通知管理人员。
告警通知的内容根据两种检测结果的不同, 如表 6 所示。