前言

参考文章学习:https://www.javasec.org/java-rasp/

基本概念

运行时应用程序自我保护(Runtime application self-protection,简称RASP)使用Java Agent技术在应用程序运行时候动态编辑类字节码,将自身防御逻辑注入到Java底层API和Web应用程序当中,从而与应用程序融为一体,能实时分析和检测Web攻击,使应用程序具备自我保护能力。

RASP技术作为新兴的WEB防御方案,不但能够有效的防御传统WAF无法实现的攻击类型,更能够大幅提升对攻击者攻击行为的检测精准度。RASP是传统WAF的坚实后盾,能够弥补WAF无法获取Web应用运行时环境的缺陷,同时也是传统Web应用服务最重要的不可或缺的一道安全防线。

RASP通过注入自身到开发语言底层API中,从而完全的融入于Web服务中,拥有了得天独厚的漏洞检测和防御条件,RASP技术相较于传统的WAF拥有了更加精准、深层次的防御。RASP采用基于攻击行为分析主动防御机制,严防文件读写数据访问命令执行等Web应用系统命脉,为Web应用安全筑建出“万丈高墙”

技术原理

JDK1.5开始,Java新增了Instrumentation(Java Agent API)JVMTI(JVM Tool Interface)功能,允许JVM在加载某个class文件之前对其字节码进行修改,同时也支持对已加载的class(类字节码)进行重新加载(Retransform)。

利用Java Agent这一特性衍生出了APM(Application Performance Management,应用性能管理)RASP(Runtime application self-protection,运行时应用自我保护)IAST(Interactive Application Security Testing,交互式应用程序安全测试)等相关产品,它们都无一例外的使用了Instrumentation/JVMTIAPI来实现动态修改Java类字节码并插入监控或检测代码。

RASP防御的核心就是在Web应用程序执行关键的Java API之前插入防御逻辑,从而控制原类方法执行的业务逻辑。如果没有RASP的防御,攻击者可以利用Web容器/应用的漏洞攻击应用服务器

示例 - Web攻击原理:

img

当Web应用接入RASP防御后,RASP会在Java语言底层重要的API(如:文件读写、命令执行等API)中设置防御点(API Hook方式),攻击者一旦发送Web攻击请求就会被RASP监控并拦截,从而有效的防御Web攻击。

示例 - RASP防御原理:

img

RASP的防御能力是基于“行为实现”的,RASP会根据Hook点触发的攻击事件(如:文件读取事件、命令执行事件)调用对应的防御模块,而不需要像传统的WAF一样,一次性调用所有的防御模块。

灵蜥Agent架构

灵蜥Agent由两大核心机制(Agent机制Hook机制)、三大核心模块(RASP LoaderRASP ContextRASP 防御模块)组成。

RASP Agent架构图:

image-20201121235014247

Agent机制和Hook机制是RASP实现防御的必要条件,RASP会使用Hook机制防御容易被攻击的Java类(如:Java SE、Web应用),当被防御的类方法被调用时会自动触发RASP的防御代码

基础概念

Hook机制类似于AOP机制(Aspect Oriented Programming,面向切面编程),使用基于Java Agent实现的Hook技术,RASP可以实现对Java类方法执行执行前后插入自定义逻辑,从而实现控制原本的程序执行的业务逻辑

ProcessBuilder Hook

未hook的原始java.lang.ProcessBuilder类代码片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.java_study.RASP;

import java.io.IOException;
import java.util.List;

public final class Hook_Test {

private List<String> command;

// 省略其他不相关类和成员变量

public Process start() throws IOException {
// 省去其他无关代码
return ProcessImpl.start(command, environment, dir, redirects, redirectErrorStream);
}

}

ProcessBuilder类可以调用UNIXProcess/ProcessImpl类的native方法执行本地系统命令,默认情况下可以被任意的Java类调用,所以存在安全问题。RASP使用Agent机制动态修改了ProcessBuilder类的start方法字节码,在方法体的前后插入RASP防御代码,当start方法被调用时因为程序逻辑已被RASP更改,必须先执行RASP的防御逻辑之后才能够执行start方法的原始业务逻辑,如果RASP调用内部的检测逻辑后发现可能存在恶意攻击,RASP会终止start方法执行逻辑,从而避免了恶意攻击

示例 - RASP的Hook逻辑代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.javaweb.rasp.agent;

import org.javaweb.rasp.agent.commons.RASPLogger;
import org.javaweb.rasp.agent.hooks.advice.RASPMethodAdvice;
import org.javaweb.rasp.agent.hooks.annotation.RASPClassHook;
import org.javaweb.rasp.agent.hooks.annotation.RASPMethodHook;
import org.javaweb.rasp.agent.utils.ClassUtils;
import org.javaweb.rasp.loader.hooks.RASPHookResult;

/**
* Hook 本地命令执行
*/
@RASPClassHook
public class LocalCommandHook {

/**
* Hook 通用的ProcessBuilder类
*/
@RASPMethodHook(className = "java.lang.ProcessBuilder", methodName = "start")
public static class ProcessBuilderHook extends RASPMethodAdvice {

@Override
public RASPHookResult<?> onMethodEnter() {
Object obj = getThisObject();

try {
// 获取ProcessBuilder类的command变量值
List<String> command = ClassUtils.getFieldValue(obj, "command");

// 将执行的系统命令转换成字符串数组
String[] commands = command.toArray(new String[command.size()]);

// 调用processCommand方法,检测执行的本地命令合法性
return LocalCommandHookHandler.processCommand(commands, obj, this);
} catch (Exception e) {
RASPLogger.log(AGENT_NAME + "获取ProcessBuilder类command变量异常:" + e, e);
}

return new RASPHookResult(RETURN);
}

}

// 省略其他本地命令执行Hook点

}

示例 - 经过RASP修改后的java.lang.ProcessBuilder类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package java.lang;

import org.javaweb.rasp.loader.hooks.RASPHookHandlerType;
import org.javaweb.rasp.loader.hooks.RASPHookProxy;
import org.javaweb.rasp.loader.hooks.RASPHookResult;

import java.io.IOException;
import java.util.List;

public final class ProcessBuilder {

private List<String> command;

public Process start() throws IOException {
// 生成Object数组对象,存储方法参数值
Object[] parameters = new Object[]{};

// 生成try/catch
try {
// 调用RASP方法方法进入时检测逻辑
RASPHookResult<?> enterResult = RASPHookProxy.onMethodEnter(parameters, ...);
String HandlerType = enterResult.getRaspHookHandlerType().toString();

if (RASPHookHandlerType.REPLACE_OR_BLOCK.toString().equals(HandlerType)) {
// 如果RASP检测结果需要阻断或替换程序执行逻辑,return RASP返回结果中设置的返回值
return (Process) enterResult.getReturnValue();
} else if (RASPHookHandlerType.THROW.toString().equals(HandlerType)) {
// 如果RASP检测结果需要往外抛出异常,throw RASP返回结果中设置的异常对象
throw (Throwable) enterResult.getException();
}

// 执行程序原逻辑,执行本地系统命令并返回Process对象
Process methodReturn = ProcessImpl.start(command, environment, dir, redirects, redirectErrorStream);

// 调用RASP方法方法退出时检测逻辑,同onMethodEnter,此处省略对应代码

return methodReturn;
} catch (Throwable t) {
// 调用RASP方法方法异常退出时检测逻辑,同onMethodEnter,此处省略对应代码
}
}

}

RASP Hook与Java Web攻击

常见的Java Web攻击方式最终几乎都会调用对应的Java类方法执行,而RASP恰好可以使用Hook机制控制任意的Java类方法执行逻辑,因此RASP可以使用Hook机制将易受攻击的Java类进行监控,从而实现防止恶意的Java Web攻击

img