RASP从0到1
字数 1818 2025-08-22 12:23:06
RASP从0到1全面指南
1. RASP概述
Runtime Application Self-Protection (RASP)是Gartner在2012年推出的一项安全技术,它通过将安全防护直接集成到应用程序中,实现对应用运行时的全面监控和保护。
1.1 RASP核心特点
- 深度集成:附加到应用程序内部,部署多个探针实现全面监控
- 实时防护:保护应用程序免受攻击,保持低误报率和性能开销
- 全面覆盖:覆盖整个应用堆栈,包括应用程序代码、库、框架和商用软件
- 上下文感知:拥有完整的上下文信息,能更深层次检查和控制应用程序
1.2 RASP与传统防护对比
| 特性 | RASP | WAF | EDR |
|---|---|---|---|
| 防护层级 | 应用内部 | 流量层 | 进程级 |
| 上下文 | 完整 | 无 | 有限 |
| 0day防护 | 有效 | 有限 | 有限 |
| 误报率 | 低 | 高 | 中 |
| 部署方式 | 应用集成 | 网络边界 | 主机 |
2. RASP环境配置
2.1 项目结构
- agent (RASP核心模块)
- javawebAgent
- test-struts2 (测试模块)
2.2 agent模块配置
pom.xml关键配置
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<configuration>
<artifactSet>
<includes>
<include>commons-io:commons-io:jar:*</include>
<include>org.ow2.asm:asm-all:jar:*</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
MANIFEST.MF配置
Manifest-Version: 1.0
Premain-Class: cn.org.javaweb.agent.Agent
Can-Retransform-Classes: true
Can-Redefine-Classes: true
Can-Set-Native-Method-Prefix: true
2.3 Tomcat虚拟机选项
-Dfile.encoding=UTF-8
-noverify
-Xbootclasspath/p:E:\CODE_COLLECT\Idea_java_ProTest\javawebAgent\agent\target\agent.jar
-javaagent:E:\CODE_COLLECT\Idea_java_ProTest\javawebAgent\agent\target\agent.jar
3. RASP核心实现
3.1 premain模式基础实现
Agent类
public class Agent {
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new AgentTransform());
}
}
AgentTransform类
public class AgentTransform implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
className = className.replace("/", ".");
System.out.println("Load class:" + className);
return classfileBuffer;
}
}
3.2 ASM字节码操作
TestClassVisitor实现
public class TestClassVisitor extends ClassVisitor implements Opcodes {
public TestClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
System.out.println(name + "方法的描述符是:" + desc);
return mv;
}
}
增强版AgentTransform
public class AgentTransform implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
className = className.replace("/", ".");
try {
if (className.contains("ProcessBuilder")) {
System.out.println("Load class: " + className);
ClassReader classReader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new TestClassVisitor(classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
classfileBuffer = classWriter.toByteArray();
}
} catch (Exception e) {
e.printStackTrace();
}
return classfileBuffer;
}
}
3.3 命令执行拦截实现
ProcessBuilderHook类
public class ProcessBuilderHook {
public static void start(List<String> commands) {
String[] commandArr = commands.toArray(new String[commands.size()]);
System.out.println(Arrays.toString(commandArr));
}
}
增强版TestClassVisitor
public class TestClassVisitor extends ClassVisitor implements Opcodes {
// ... 其他代码同上 ...
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("start".equals(name) && "()Ljava/lang/Process;".equals(desc)) {
return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
@Override
public void visitCode() {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "java/lang/ProcessBuilder", "command", "Ljava/util/List;");
mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/agent/ProcessBuilderHook",
"start", "(Ljava/util/List;)V", false);
super.visitCode();
}
};
}
return mv;
}
}
4. 表达式注入监测
4.1 表达式注入监测实现
Agent类实现
public class Agent implements Opcodes {
private static List<MethodHookDesc> expClassList = new ArrayList<>();
static {
expClassList.add(new MethodHookDesc("org.mvel2.MVEL", "eval",
"(Ljava/lang/String;)Ljava/lang/Object;"));
expClassList.add(new MethodHookDesc("ognl.Ognl", "parseExpression",
"(Ljava/lang/String;)Ljava/lang/Object;"));
expClassList.add(new MethodHookDesc(
"org.springframework.expression.spel.standard.SpelExpression", "<init>",
"(Ljava/lang/String;Lorg/springframework/expression/spel/ast/SpelNodeImpl;" +
"Lorg/springframework/expression/spel/SpelParserConfiguration;)V"));
}
public static void premain(String agentArgs, Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
final String class_name = className.replace("/", ".");
for (final MethodHookDesc methodHookDesc : expClassList) {
if (methodHookDesc.getHookClassName().equals(class_name)) {
final ClassReader classReader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
final int api = ASM5;
try {
ClassVisitor classVisitor = new ClassVisitor(api, classWriter) {
@Override
public MethodVisitor visitMethod(int i, final String s, String s1,
String s2, String[] strings) {
final MethodVisitor mv = super.visitMethod(i, s, s1, s2, strings);
if (methodHookDesc.getHookMethodName().equals(s) &&
methodHookDesc.getHookMethodArgTypeDesc().equals(s1)) {
return new MethodVisitor(api, mv) {
@Override
public void visitCode() {
if ("ognl.Ognl".equals(class_name) ||
"org.mvel2.MVEL".equals(class_name)) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
} else {
mv.visitVarInsn(Opcodes.ALOAD, 1);
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
Agent.class.getName().replace(".", "/"),
"expression", "(Ljava/lang/String;)V", false);
}
};
}
return mv;
}
};
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
classfileBuffer = classWriter.toByteArray();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
return classfileBuffer;
}
});
}
public static void expression(String exp_demo) {
System.err.println("---EXP---");
System.err.println(exp_demo);
System.err.println("---调用链---");
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for (StackTraceElement element : elements) {
System.err.println(element);
}
System.err.println("---");
// 阻断攻击
throw new SecurityException("Detected malicious expression: " + exp_demo);
}
}
MethodHookDesc辅助类
public class MethodHookDesc {
private String hookClassName;
private String hookMethodName;
private String hookMethodArgTypeDesc;
// 构造方法、getter和setter
}
5. agentmain动态附加实现
5.1 agentmain核心实现
attachAgent类
public class attachAgent {
public static void agentmain(String agentArgs, Instrumentation inst)
throws UnmodifiableClassException {
CustomClassTransformer transformer = new CustomClassTransformer(inst);
transformer.retransform();
}
}
CustomClassTransformer类
public class CustomClassTransformer implements ClassFileTransformer {
private Instrumentation inst;
public CustomClassTransformer(Instrumentation inst) {
this.inst = inst;
inst.addTransformer(this, true);
}
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
System.out.println("In Transform");
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public MethodVisitor visitMethod(int i, final String s, String s1,
String s2, String[] strings) {
final MethodVisitor mv = super.visitMethod(i, s, s1, s2, strings);
if ("say".equals(s)) {
return new MethodVisitor(Opcodes.ASM5, mv) {
@Override
public void visitCode() {
super.visitCode();
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System",
"out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("CALL " + s + " method");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/io/PrintStream",
"println", "(Ljava/lang/String;)V", false);
}
};
}
return mv;
}
};
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
}
public void retransform() throws UnmodifiableClassException {
Class[] loadedClasses = inst.getAllLoadedClasses();
for (Class clazz : loadedClasses) {
if ("cn.org.javaweb.agent.attach.MainTest".equals(clazz.getName())) {
if (inst.isModifiableClass(clazz) &&
!clazz.getName().startsWith("java.lang.invoke.LambdaForm")) {
inst.retransformClasses(clazz);
}
}
}
}
}
5.2 动态附加程序
Attachit_execinRun类
public class Attachit_execinRun {
public static void main(String[] args) throws Exception {
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : list) {
if (vmd.displayName().endsWith("MainTest")) {
VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
virtualMachine.loadAgent("agent.jar路径", "Attach!");
virtualMachine.detach();
}
}
}
}
5.3 agentmain的MANIFEST.MF配置
Manifest-Version: 1.0
Agent-Class: cn.org.javaweb.agent.attach.attachjar.attachAgent
Can-Retransform-Classes: true
Can-Redefine-Classes: true
Can-Set-Native-Method-Prefix: true
6. RASP实现中的关键问题
6.1 字节码操作关键点
-
局部变量表访问规则:
- 静态方法中:0指代第一个参数,1指代第二个参数
- 实例方法中:0指代this,1指代第一个参数
-
方法描述符解析:
(Ljava/util/List;)V:接收一个List参数,返回void()Ljava/lang/Process;:无参数,返回Process对象
-
关键字节码指令:
ALOAD:加载引用到操作数栈GETFIELD:获取实例字段值INVOKESTATIC:调用静态方法INVOKEVIRTUAL:调用实例方法
6.2 RASP实现挑战
-
premain vs agentmain:
- premain:启动时加载,可添加方法
- agentmain:运行时附加,不可添加方法
-
类重定义限制:
- 不能添加/删除方法
- 不能更改父类
- 不能更改接口
- 不能更改方法签名
-
性能考量:
- 尽量减少transform操作
- 使用COMPUTE_MAXS自动计算最大栈和局部变量
- 避免不必要的类转换
7. RASP实践中的思考
7.1 RASP的优势
- 上下文感知:拥有完整的执行上下文信息
- 0day防护:对未知漏洞有一定防护能力
- 低误报率:基于实际行为而非模式匹配
- 供应链安全:能监控第三方库的行为
7.2 RASP的局限性
- 语言限制:不同语言需要不同实现
- 部署复杂性:需要侵入应用
- 稳定性风险:错误可能导致应用崩溃
- 自身安全:RASP本身可能成为攻击面
7.3 RASP最佳实践
- 与WAF/EDR协同:构建纵深防御体系
- 规则精细化:针对业务定制规则
- 性能优化:减少对业务的影响
- 安全加固:确保RASP自身安全
8. 总结
RASP作为一种深度集成的运行时防护技术,能够提供传统边界防护无法实现的精细化安全控制。通过字节码插桩技术,RASP可以在关键API调用点插入安全检查逻辑,实现对命令执行、表达式注入等攻击的有效防护。
实现一个完整的RASP解决方案需要考虑:
- 选择合适的hook点
- 设计高效的字节码转换逻辑
- 构建完善的规则体系
- 确保部署和运行的稳定性
- 与其他安全产品协同工作
随着应用安全需求的不断提升,RASP将在应用安全防护体系中扮演越来越重要的角色。