编程语言
首页 > 编程语言> > 洞态IAST源码分析及吐槽

洞态IAST源码分析及吐槽

作者:互联网

0x01 前情提要

北京时间11点42分,正准备划 20 分钟水去吃午饭,园长突然跟我说过火线的 IAST 突然开源了,原名灵芝IAST更名为洞态IAST,真是OMG。

IAST 也是同样使用 agent 技术。同样一种技术,在不同人的手里用法也不同,同一种思路在不同人手里的实现方式也可能存在差异。那么既然他开源了,那就来看一看学习一下他的思路和实现,也顺便测试一下产品,取其精华。

能有这样的产品开源供广大安全从业者测试和学习真的是一件非常好的事,真的是要撒花庆祝,感谢老板。由于明确项目定位是 IAST 产品,因此主要关注的是检测能力的实现和 sink/source/hook 点的选取。

项目地址:[https://github.com/HXSecurity/DongTai-agent-java][0]

文档地址:https://github.com/HXSecurity/DongTaiDoc

0x02 项目结构

首先看一下整个的代码逻辑,项目总共分为 3 个模块,分别是 iast-agent、iast-core、iast-inject。

iast-agent

入口类是 com.secnium.iast.agent.Agent,与任何一家使用 java agent 技术的产品一样,洞态也是使用了 Sun JVM Attach API 将 agent 附加到指定的 Java 进程上。

img

com.secnium.iast.agent.IASTProperties 是 agent 的单例配置类,从 src/main/resources/iast.properties 中读取配置。

com.secnium.iast.agent.IASTClassLoader 是 agent 自定义的 ClassLoader,继承自 URLClassLoader,这个类的代码参考自 jvm-sandbox,其中需要注意的是,在卸载 agent 时需要关闭 ClassLoader,如果不能通过 ClassLoader 的 close() 方法进行关闭,则需要寻找已经打开的 jar 文件并释放文件句柄。

com.secnium.iast.agent.UpdateUtils 是由客户端主动向云端发送请求,用来检查版本更新和发送更新状态,其中静态方法 sendRequest() 可以向外发送 Http 请求。也可以看到发送请求使用了 ua: SecniumIast Java Agent,这部分其实可以做为一个特征。

com.secnium.iast.agent.AgentLauncher 是在应用程序指定了 javaagent 参数情况下的入口类,这个类中的 premain 和 agentmain 方法均调用了共同的 install() 方法安装 agent。这个方法则是调用 com.secnium.iast.agent.manager.EngineManager 对整个流程进行管理。

img

接下来我们看一下 EngineManager 这个类,这个类是 IAST 引擎管理器,并且使用单例对象,首先执行的是 updateEnginePackage() ,更新 IAST 引擎需要的 jar 包,从云端进行下载。

然后调用 install() 方法,首先将 iast-inject.jar 注册到 BootstrapClassLoader 中,然后使用自定义的 IASTClassLoader 加载检测引擎 iast-core.jar ,并反射调用里面的 com.secnium.iast.core.AgentEngine 的 install() 进行检测逻辑的初始化和加载动作。

img

进一步加载检测引擎中的多个引擎:

img

然后反射调用com.secnium.iast.core.AgentEngine 的 start() 方法,更新在检测引擎中的一个全局标识位。

img

检测引擎启动之后,agent 端还会启动几个守护线程:

img

这个模块就是起到一个 agent 入口的作用,无需多言。

iast-inject

这个模块只有一个类 java.lang.iast.inject.Injecter,难道不应该是 Injector 吗,害。

这个类定义了一些回调方法钩子:

img

并围绕这些钩子定义了若干方法,这些放在将会使用 ASM 时插入指定类的类字节码中。

img

这个类实际上是定义中间处理逻辑以及定义供 ASM 调用的方法。代码太长了,不想细看。

iast-core

这个模块如其名字,是整个 agent 的核心,我们先来看一下整个项目的目录结构:

另外,在这个项目的 resources 中,还有一些基础的 txt 以及 xml 的配置,这部分等调用到此的时候再说。

首先来继续之前的调用流程,iast-agent 通过反射调用com.secnium.iast.core.AgentEngine 的 install() 方法,调用各个引擎的 start() 方法。此处关注其中几个引擎:

ConfigEngine 通过请求 /api/v1/profiles,并解析其中的结果,最后创建了一个 IASTHookRuleModel 实例,这个实例就是这个 agent 的处理模型,其中保存了很多处理中用到的规则。

img

TransformEngine使用 Instrumentation 接口,进行字节码的转换,调用了com.secnium.iast.core.enhance.IASTClassFileTransformer#retransform 方法。

img

这个方法使用了 IASTClassHookPointMatcher#findForRetransform() 使用 Instrumentation 对象的 getAllLoadedClasses() 获取所有已经被加载的类,并通过 com.secnium.iast.core.util.matcher.ConfigMatcher#isHookPoint() 方法进行筛选判断,返回了一个需要修改的类的 List 。

img

通过以上代码可知,agent 对以下的类没有 hook:

在 blacklist.txt 中写了多达 7 万多行的类名和前后缀,根据其注释,这是为了过滤掉 Sandbox 所需要的类,防止 ClassCircularityError 的发生。

随后调用 retransformClasses() 会让类重新加载,从而使得注册的类修改器能够重新修改类的字节码,这要就会调用之前通过 addTransformer() 注册的 IASTClassFileTransformer 中重写的 transform() 方法。

方法里首先调用 com.secnium.iast.core.enhance.IASTClassAncestorQuery#scanCodeSource 通过获取 jar 包中的 manifest 信息并将其发送回云端,这部分是 SCA 功能的实现。

然后二次调用了 ConfigMatcher.isHookPoint() 判断 hook 类,感觉这个判断写重了,没必要。

在 IASTClassAncestorQuery 里缓存了 CodeSource/ClassLoader/ClassName/SuperName/Interfaces。

创建 ClassWriter,依然是使用 COMPUTE_FRAMES 自动计算帧的大小,并且重写了getCommonSuperClass() 方法,在计算两个类共同的父类时指定ClassLoader。

创建 IASTContext 上下文,初始化 PluginRegister,这个类中包含了一个全局常量 PLUGINS,里面保存了很多的处理插件,这些类都实现了 DispatchPlugin 接口,这个接口包含两个方法:

img

在上图的类中是 DispatchPlugin 的实现类,其中包含了 agent 中的一些 sink/source/hook 点,在这些类的 dispatch() 方法中,会创建继承至 AbstractClassVisitor 的各个ClassVisitor,在 ClassVisitor 中又通过重写 visitMethod() ,注册继承至 AbstractAdviceAdapter 的实现类,这些类重写父类的 before()/after() ,实际上是 AdviceAdapter 的 onMethodEnter()/onMethodExit() 实现了字节码的插入。具体的字节码插入部分是 ASM 的 API ,无需多言。

这里可以看到,洞态为每种不同的 hook点/sink点/source点订制了不同的 ClassVisitor 和 MethodVisitor,也就是说写入的字节码不一致,那到底写入了什么呢?通过看 ASM 的 API 比较难以阅读,还是在字节码写入后把 class dump 出来看比较方便。

SpyEngine 通过调用 java.lang.iast.inject.Injecter#init 方法将 com.secnium.iast.core.handler.EventListenerHandlers 中定义的全部处理方法存入了 namespaceMethodHookMap 中供全局调用。而在 EventListenerHandlers 中定义的这个方法,实际上又是由 Injecter 通过反射调用。

而后续系统的全部功能,都是由 EventListenerHandlers 中定义的这些方法处理和调度的,这里不再进行一一分析。

0x03 功能实现探究

支持漏洞

洞态 IAST 支持的漏洞类型位于 com.secnium.iast.core.handler.vulscan.VulnType,如下图:

img

对应配置文件中的 model.xml,通过反向查找调用就可以查看相关的处理逻辑,各位看官请自行评测,本文不会对每种漏洞的实现进行一一介绍。

SCA 实现

一个优秀的 IAST 一定有 SCA 一类的功能,简单的实现都是通过收集客户段组件信息,发送到云端通过匹配 CPE,并链接到对应的 CVE/CWE/CNNVD 等,并进行展示,先看一下洞态的云端效果,在组件管理:

img

看看右边的数量和左边的数量完全对应不上,难道是我对这些数字的理解有问题?点击进入条目,有该组件对应的一些信息的展示

img

再点击就有对应 CVE 的一些漏洞信息描述的信息。

img

那么这个功能是如何实现的呢?在前面提到 SCA 是由com.secnium.iast.core.enhance.IASTClassAncestorQuery#scanCodeSource 所实现,这个方法有两个出口,据我判断,应该是不会走到下面那个 scan() 方法。

img

com.secnium.iast.core.enhance.sca.ManifestScaner#parseJarManifest 调用 getPackgeInfo 获取 Attributes 中的 Implementation-Version 和 Implementation-Title

img

最后拼接出了一些对应的信息发送给云端。

img

云端接收到这些信息处理入库,并对接自己 CPE/CVE 漏洞信息库进行分析和展示。

这部分实际上是非常简单的实现,没有复杂的检查逻辑,这个功能 OWASP 有开源的,建议参考:https://github.com/jeremylong/DependencyCheck

sink/source/propagator/http

构建一个 IAST,重要的就是整个模型的构建,前面分析过,模型的构建是通过 buildRemote() 方法获取远端的配置。

img

由于这个 json 太长了,我没细看,在这里就不展示了,这个配置在本地也有一个 model.xml,以 xml 格式储存了这些信息,这些信息在处理后会被转为 IASTHookRuleModel 对象。

在这个配置中我们发现了一些标记,他们都代表什么呢?

其中 sink 点要标记污点所在的参数位置,传播节点要标记源位置和目标位置。分类处理完这些配置文件后,将会将所有信息保存到 IASTHookRuleModel 中的一些变量中。

img

在之前的分析中就提到过,对于每一种不同的 hook 点,插入的字节码是不一样的。

sink 点插入:

image-20210517191747588

传播节点:

image-20210517191807625

source节点:

image-20210517191824567

http节点:

image-20210517191844841

这其中的逻辑,总结起来是这样的,系统中定义了了一个 com.secnium.iast.core.handler.controller.TrackerHelper,用来作为一个全局的计数器?(追踪器),当进入一个节点时,对应的成员变量会自增 1,而退出时,会 -1,并且判断当前节点是否为第一层级节点,如果不是,将不会走入后续 spyMethodOnBefore() 方法。这个类中还定义个一个 trackCounts,目前还没有用上。

在 spyMethodOnBefore() 方法中,将会 Hook 点类型的不同分别调用 HttpImpl.solveHttp、PropagatorImpl.solvePropagator、SourceImpl.solveSource、SinkImpl.solveSink 进行不同点的处理。

http 节点处理

创建一个自定义的 com.secnium.iast.core.util.http.HttpRequest 对象,储存相关内容和 httpServletRequest 引用对象,静态文件不处理,目前定义的静态文件后缀是 .js,.css,.htm,.html,.jpg,.png,.gif,.woff,.woff2,.ico,.maps,.xml,看到了 htm/html 没有处理,这种情况下伪静态的网站可能会漏检查,根据正则查看 url 中是否含有 “login” 字样,并设置其是否为登陆 URL,将一些信息初始化和缓存到 EngineManager 中。这类节点主要负责标记和预处理的。

sink 节点处理

使用程序启动时加载的 IASTHookRuleModel,在其中获取方法签名对应的 sink 方法对象,这里返回的是一个 IASTSinkModel 对象,调用 com.secnium.iast.core.handler.vulscan.ScannerFactory#preScan 进行数据预处理,预处理主要是包括对 unvalidated-redirect 和 sql-over-power 两种漏洞类型的处理。

预处理之后,调用 com.secnium.iast.core.handler.vulscan.ScannerFactory#scan,又分别进行动静态的扫描:

source 节点处理

将当前污点来源事件存入 EngineManager.TRACK_MAP 中,将污点来源的返回结果放入 EngineManager.TAINT_POOL 污点池中。

propagator 节点处理

处理传播节点的逻辑是最复杂,这里还是简单描述:

看完了这些节点的处理方式,我们简单串一下逻辑:

  1. 一次请求到达了应用程序,首先进入 http 节点处理逻辑,进行标记和预处理。
  2. 请求进入到 source 点,将 event 放入 EngineManager.TRACK_MAP 中,将 source 的结果放入了 EngineManager.TAINT_POOL 污点池中。
  3. 请求再进入 propagator 节点时,根据配置判断传播节点的参数是否存在于污点池中,如果是,则将传播节点 event 放入 EngineManager.TRACK_MAP 中。
  4. 应用程序走到最后的 sink 点时,根据 sink 点的配置,判断 sink 点的参数是不是在 TAINT_POOL 中,如果是,则将 sink 点写入 EngineManager.TRACK_MAP 中。
  5. 随着程序的多次调用,程序还会再次进入多次传播节点,这些节点也会被放入 EngineManager.TRACK_MAP 中。
  6. 在应用程序执行完,回到 http 节点,最后执行到 leaveHttp 时,会调用 GraphBuilder 构造污点调用图并发送至云端。

越权检测

在看代码的时候,多次看到 over power 一类的字样,想来想去终于想明白了,这可能是越权的意思。IAST 能检测越权?有点意思,那我们来看看他是如何实现的。

之前提到过 com.secnium.iast.core.handler.vulscan.ScannerFactory#preScan,在这个位置命中 sink 点后,有两个处理逻辑:

  1. 如果命中了 unvalidated-redirect 的 sink 点,并且是方法签名是 setHeader/addHeader 等,就将其理解为可能是登陆成功后的跳转操作,找到其中 Set-Cookie 的值:
  1. 将会创建 IJdbc 的实例, 调用LoginLogicRecognize.handleLoginLogicRecognize() 方法处理登陆逻辑识别:

后续在之前也看到了,对应的 OverPowerScanner 的 scan 方法没有具体实现,那这时候我们可以猜测一下,作者主要是想通过 cookie 和登陆的 sql 语句进行关联。通过检查 sql 语句是否与污点池有关、检查 sink 点的参数是否与污点池有关来判断是否有越权。这些数据都被发送到了云端,那对于云端来说,如何区分不同权限的用户?如何判断这个请求是否应该匹配到用户?

以目前程序里的处理逻辑,还做不到鉴权的功能,在云端中也没有展示这个漏洞类型,等待进一步的更新。

其他

其他漏洞的检测逻辑没什么要说的,主要是 hook 点的选取。

0x04 测试

在看完代码逻辑和简单试用后,我们正式的测试一下这个产品。
首先编译一下 agent,我这边的环境是:

通过调试发现自己编译 core 和 inject 没用,他的 agent 无论如何还是会从官网上自己下载这两个 jar 包并放到 temp 目录下,不知道是故意的还是写出来的 bug,因为如果从官网上下载,也只是下载 agent,在运行时动态下载 core 和 inject,应该是最开始打算试用,没打算开源??由于我的目的是学习调试,所以我使用了自己编译的关闭了 proguard 混淆的版本,并修改了判断逻辑,使应用程序不去云端判断,直接加载本地 jar。

为了更直观的看到 agent 对类字节码的更改,需要在配置文件中更改 iast.dump.class.enable 和 iast.dump.class.path 相关参数。

功能型测试

通过上一章功能实现的探究,我们已经关注到了几个安全检测功能的实现,那具体的检测结果怎么样呢?

img

我在自己的靶场里触发了绝大部分的漏洞类型,但很遗憾的是,由于云端的搜索引擎问题,以及 sink 点选取问题,我没能在云端看到太多的检出漏洞。云端的漏洞展示是有问题的,看不到前一天的漏洞内容,不知道是 django 的问题还是什么,建议修复一下。

也建议作者出一个官方漏洞靶场,能对应到所有洞态支持的漏洞类型,也容易理解和说明。

性能测试

以下是使用 wrk 进行的压测:

img

可以看到洞态给应用程序性能带来的影响特别大,当然 IAST 通常都在测试环境下使用,所以可能并不是特别关注性能。

0x05 评价

出来在之前分析过程中的一些我将从两个方面对目前版本洞态 IAST 进行评价,首先是使用中的一些想法:

  1. SCA 漏洞组件管理没有整理和去重,在我测试的过程中多次重启项目导致同一个条目在云端能看到很多次。
  2. 页面上表格查询和相关排序用起来真的难受,建议招一个设计。
  3. 搜索功能面对小白非常不友好,可以说如果对 IAST 不了解的情况完全用不了这个管理后台。
  4. 支持的漏洞数量还是少了点,并且没有对这种漏洞的描述、解决建议什么的信息。
  5. 既然是 java agent 技术的产品,那应该能够给到用户完整的调用链、一些关键调用点信息如代码行数等等,而不是只给到一个 http 请求。

作为一名安全研究员,同时也是 RASP 产品的参与者,我提出几点想法:

  1. 所有的 hook 点,全部是写死的规则文件,无论是本地的 xml 也好,还是远端的 json 也好,都将 hook 点的类,描述符,相关信息完全写死,我相信这些规则是通过某些手段生成出来的,但是一旦在 hook 点选取上没有选择使用动态手段,那就失去了和 0 day 打交道的能力。
  2. hook 点(这里指 sink )的选取还是层次太浅、规律性较差。
  3. 洞态 IAST 检测了当前环境使用的中间件,并发送给云端,目前除了信息收集还没看到有什么样的具体用途,但是通过hook点来看,对于http请求的点还是使用了适配各个中间件的方式, OpenRASP 也是采用这种方式,这种方式在功能上没有什么问题,但是同样地还是失去了动态性,不优雅,也不能做到通用。
  4. 应用在实现上使用了太多的字符串比较处理,以及正则,这将对原应用性能带来极大的影响。
  5. 在调试过程中,包括污点图的处理,总是被大量的无效信息占用了过多的时间,比如 StringBuilder 一类的传播节点,这其实不是漏洞调用的关键节点,个人认为没有必要处理他。
  6. agent 每次收到请求,只要不是静态的,都要向云端发送报告,丧心病狂吧。
  7. 没有对 ClassLoader 进行相关处理,无论是前两年的冰蝎,还是各种反序列化的利用 gadget ,包括我自己的 su18.jsp ,都少不了使用 ClassLoader 加载恶意类的请求,这应该目前 Java 安全关注点比较高的地方,还是建议给 IAST 一个挖 gadget 的可能。
  8. 攻击者目前常用的类似内存马、动态注册 Filter 一类的、以及像一些反序列化恶意类找 response 对象回显的,其实都可以试着搞一搞。

就这样吧,也不想说太多了。

0x06 吐槽

通过个人角度,主要觉得这个项目有以下槽点:

img

img

img

img

img

img

img

img

img

img

0x07 总结

对于一款使用 Java agent 技术开发的工具/产品,重中之重就是 hook 点的选取,以及处理各项逻辑的具体实现,因为需要将代码运行到服务器端,不影响原有功能、不影响原有性能是首要考虑的目标。

这是作为框架考虑的,其次是针对各个漏洞点的检测逻辑,这部分是需要对漏洞理解的足够深入,也就是需要安全研究人员的介入,目前洞态有一部漏洞的检测是写死在代码里的,一部分漏洞的检测是依靠配置文件的设置的。还没有处理成大家习惯的框架-插件的模式,所以想在此基础上二开还是需要花费较多时间理解代码。

极简简约动态分割线本周六快手联合火线 举办线下「观火」白帽沙龙活动 ↓扫描二维码火速了解解码未来活动banner

【火线Zone】

火线Zone是[火线安全平台]运营的封闭式社区,社区成员必须在[火线安全平台]提交有效漏洞才能申请免费加入,符合要求的白帽子可联系[火小表妹]免费加入~

我们不希望出现劣币驱逐良币的结果,我们不希望一个技术社区变成一个水区!

欢迎具备分享精神的白帽子加入火线Zone,共建一个有技术氛围的优质社区!

指纹识别简约卡片动态二维码 (2)

引导分享点赞在看GIF引导三连

标签:调用,iast,secnium,agent,源码,及吐槽,sink,洞态,com
来源: https://blog.csdn.net/weixin_40418457/article/details/117121346