RASP技术实现

0x00 概念

运行时应用自我保护,RASP(Runtime Application Self-Protection)是一种植入到应用程序内部或其运行时环境的安全技术。它能够控制应用程序的执行流程,并且可以实时检测和阻止漏洞攻击行为。该技术可以用来通过自我保护的措施来阻止相关网络攻击,或在没有人为干预的情况下自动重新配置环境,以此来解决特定网络问题(威胁,故障等)。

WAF原理和RASP原理做比较:
WAF在边界进行规则匹配。 RASP技术在应用程序内部进行探测和规则分析,更为精准。优点:1.误报率低。2.可以防护0Day级别的漏洞攻击。缺点:1.对应用程序性能有消耗。理论上是5%~10%。

0x01 Java版本技术实现原理(OpenRASP分析)

RASP技术实现实质是在不接触应用源码的情况下,对函数进行Hook操作。 而JVM提供了接口(Instrument机制),让我们操作字节码。而操作字节码用到的库,一般有ASM、Javassist等。ASM提供底层字节码操作、效率性能好。而Javassist库封装了底层操作,更加方便开发实现,百度的OpenRASP产品就是使用Javassist实现字节码修改的。 这里通过阅读OpenRASP源码(很多干货),来看看如何防护SSRF攻击的。 基于Instrument机制的字节码编织,会到rasp-boot的Agent类premain方法。

1
2
3
4
5
6
7
8
9
 /**
* 启动时加载的agent入口方法
*
* @param agentArg 启动参数
* @param inst {@link Instrumentation}
*/
public static void premain(String agentArg, Instrumentation inst) {
init(START_MODE_NORMAL, START_ACTION_INSTALL, inst);
}

紧接着会进入EngineBootstart方法进行初始化,initTransformer(inst);就是对Java字节码进行修改的具体代码了。
这里会扫描com.baidu.openrasp.hook包下所有带注解@HookAnnotation的类,每个类负责Hook一个/多个Method。提取Method的参数,使用Javassist技术增加检测代码并植入到目标Class文件。 CustomClassTransformer类的transform方法是编织字节码的入口,加载类文件时,都会调用这个方法,判断是否时需要修改编织的类。如果是,则修改完返回字节码流。 我框出来的代码部分会调用每个Hook类的hookMethod方法。SSRF防护时,会Hook所有常见发送网络请求的函数。这一步主要获取请求参数,结合用户网络请求输入的参数,进行关联比对分析。 上图使用了Javassist库,在org/apache/commons/httpclient/URIparseUriReference方法后面添加了1行代码,调用CommonHttpClientHook.checkHttpConnection方法。参数$0$1表示调用栈里面的参数,也就是当前URI类的实例(Object)和第1个参数String original(即解析的URL)。 接着,将通过Hook函数获取的URL、HostName等重要信息放在Map中,供检测攻击使用。Checker代码大部分作为扩展在JS里面实现,而SSRF检测代码在Java中实现了。 可以看到,检测算法是:

  • 遍历每个request层面拦截到的用户输入参数(parameterMap),与拦截的URL比较。如果相同,则可判断是用户输入的URL到达了底层网络请求的API。
  • 检测URL对应的IP是否是内网地址。如果是,则判定为SSRF攻击。
  • 根据配置,攻击Block。可以通过Hook返回的Respone里面,都存储在ThreadLocal。

小结下,过程是:Java Instrument机制 -> Javassist库修改字节码Hook关键函数 -> 通过比对用户Request的参数和底层网络请求参数,检测攻击。

0x02 基于JVM-Sanbox的实现

OpenRASP开源实现已经很赞了,还有很多商业功能,用到实际业务中实践没问题。这里我为了完成相关研究测试,及平常简单使用。使用阿里的JVM-Sandbox快速简洁地实现了2个工具,分别用来禁用命令执行防护通用RCE/SQL注入攻击的RASP实现。 Java禁用掉命令执行API,可以防护很多安全攻击。在2次的DDCTF题目中,我都用了这个,线上运行无bug。由于JVM-Sandbox提供了良好的API支持,代码优雅而简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Http("/blockSystemExec")
public void blockSystemExec() {
new EventWatchBuilder(moduleEventWatcher)
.onClass("java.lang.ProcessBuilder")
/**/.includeBootstrap()
.onBehavior("start")
.onWatch(new AdviceListener() {
@Override
public void before(Advice advice) throws Throwable {
String cmd = "echo 'your are a good man'";
Runtime run = Runtime.getRuntime();//返回与当前 Java 应用程序相关的运行时对象
Process p = null;
try {
p = run.exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
// 然后立即返回,因为监听的是BEFORE事件,所以此时立即返回,方法体将不会被执行
ProcessControlException.throwReturnImmediately(p);
}
}
);
}

命令执行,包括Runtime.exec都是走到java.lang.ProcessBuilderstart方法。运行过程中,只要知道进程号,就能拦截命令执行,效果如下:
RASP我实现了通用RCE检测以及参考OpenRASP算法实现SQL注入的检测。主要目的是研究和测试,RCE检测和拦截实现是针对性的分析Spring、Struts2框架历史漏洞,找到关键API进行Hook分析。与OpenRASP不同的是,针对性的加入了多层次监控(如:OGNL、SpEL语言层),整体当时框架设计是:

0x03 参考

Java动态编程初探——Javassist
https://github.com/baidu/openrasp
禁用命令执行
防护通用RCE/SQL注入攻击的RASP实现

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2019/05/12/rasp-technology-implementation/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog