RASP(Runtime Application self-protection)技术自Gartner在2014年提出,已经有挺长一段时间了。大厂们现在基本也都有自研的RASP、IAST产品。在开源RASP中,应以OpenRASP起步最早,应用也最广。不过因为性能原因,在实际攻防中覆盖面还是有限。
RASP算是Java安全绕不开的一个技术点,有兴趣对此学习研究一番。参考文章中threedr3am、tr1ple、c0d3p1ut0s几个师傅们对于OpenRASP的原理、技术点都有跟踪和解释,因此本文不再多做类似的阐释,主要针对自己的一些知识盲点、兴趣点,做做研究与笔记。
OpenRASP的Hook
关注两点:
- 如何Hook的
- Hook了哪些类的哪些方法
如何Hook
官方文档已经描述的很详细了
注意步骤2 Retransfrom,第一次遇到
Instrumentation
Java Instrumentation APi支持启动时/运行时Attach Agent,具体看个简单例子
1 | public class AgentMainTest { |
(这个是beyond大佬以前MemShell的代码),需要注意的是premain以及transform实现接口,大同小异,OpenRASP的这个逻辑是在EngineBoot#start开始的,具体的代码就不贴了,流程跟踪如下
1 | EngineBoot#start |
OpenRASP具体的transform逻辑在AbstractClassHook的各种子类中实现
Javassists
transform内是通过Javassists字节码技术,实现对类方法的修改/插桩。以CommonHttpClientHook为例,整个调用链如下
1 | AbstractClassHook#transformClass |
具体关键代码如下
1 |
|
通过javassist在org.apache.commons.httpclient.URI#parseUriReference方法后加载了checkHttpConnection这个方法代码块。
这个checkHttpConnection功能则为攻击检查分析逻辑,后面详细讲。
Hook了哪些类方法
官方文档中有列出详细Hook了哪些函数 Hook 函数列表,如下
继承AbstractClassHook且有HookAnnotation注解的类,他们的hook逻辑都会被加载,具体筛选代码如下
1 | private void addAnnotationHook() { |
OpenRasp的检测逻辑
在hook插入的代码逻辑,一般都是预处理的逻辑,真正判断是否是恶意poc,是在OpenRASP的checker逻辑中。
checker
还是以CommonHttpClientHook SSRF为例,hook插入了checkHttpConnection函数代码块
1 | public static void checkHttpConnection(Object object, String url) { |
checkHttpConnection最终会调用Checker,进行规则判断,调用过程如下
1 | CommonHttpClientHook#checkHttpConnection |
Cheker类型可以大致分为3类
- js插件检测,如SQL、命令执行、XXE、SSRF之类
- java本地检查,目前仅2个
- 安全基线检测,主要是类似配置检查,如弱口令之类
v8
以上的Checker中,以V8AttackChecker最重要。至于为何要用JS检测引擎作为核心,个人认为主要原因应该是支持跨语言、热部署。
install openrasp-v8 in mac
官网的安装脚本是linux的,这里给下max下需要变动的命令
1 | mkdir -p ../java/src/main/resources/natives/osx_64 && cp java/libopenrasp_v8_java.dylib $_ |
JS引擎
官方文档还是Rhino的流程(这里和v8大致是一样的,细节不同)
主要有两部分
- openrasp-v8的js库
1 | ├── console.js console.log实现 |
这些库可以在js插件中进行调用
- js插件
检测逻辑都是在js插件内实现,初始化流程如下
1 | EngineBoot#start |
其中V8.CreateSnapshot/ExecuteScript都是在openrasp-v8 jni内实现的,未深入跟踪。
其中JS.UpdatePlugin默认会加载plugins下面的js。还有InitFileWatcher实时监控js插件文件变动,这个师傅们有讲这里也不讲了。
check
V8AttackChecker到v8的流程如下
1 | V8AttackChecker#check |
V8.Check也是jni实现,暂无力跟踪,猜测是会根据type,调用对应的js插件内的check函数,如type=ssrf,就会调用official/plugins.js内的plugin.check_ssrf。
OpenRASP的绕过
参考【1】中有提到HookHandler.enableHook这个属性,通过反射设置为false关闭Hook功能,达到OpenRASP的绕过。
从原理上大致脑爆下可能存在的攻击点,限于精力,未做验证。
- 可以根据OpenRASP Hook&Check逻辑上的一些控制变量,达到绕过效果,如上面的enableHook。
- 可以退出OpenRASP,如调用EngineBoot#release
- 可以搞瞎V8,如V8.stackGetter
- 可以删除js规则,如plugins/xxx.js
- 针对性绕过js插件检测逻辑
小结
在阅读师傅们文章的基础上,记录了自己在纯静态读OpenRASP源码时感兴趣的一些知识点,大致对其架构原理有了一定的了解。
个人认为最主要的点还是在v8引擎以及插件的检测逻辑上,但是本篇重点没有在此,只是简要过了一遍整个架构及原理。后续如果有精力,可能会在此基础上再研究下绕过的知识。
参考【1】中提到openrasp类优先加载影响应用类的问题,印象中商业版好像是做了解决,找不到群聊记录了。
最后有趣的八卦下:openrasp一开始用的是v8+j2v8,0.20因为性能原因改成了Rhino,1.1又改回了v8+自研的openrasp-v8,感觉是一段精益求精的历史,很有意思。
参考
- [1] OpenRASP核心源码浅析
- [2] Java openrasp学习记录-2
- [3] Java openrasp学习记录-1
- [4] Java RASP浅析——以百度OpenRASP为例
- [5] 系统架构 - Java 版本
- [6] Hook 函数列表
- [7] GitHub OpenRASP
- [8] GitHub OpenRASP-v8
- [9] 浅谈 RASP