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 字节码操作关键点

  1. 局部变量表访问规则

    • 静态方法中:0指代第一个参数,1指代第二个参数
    • 实例方法中:0指代this,1指代第一个参数
  2. 方法描述符解析

    • (Ljava/util/List;)V:接收一个List参数,返回void
    • ()Ljava/lang/Process;:无参数,返回Process对象
  3. 关键字节码指令

    • ALOAD:加载引用到操作数栈
    • GETFIELD:获取实例字段值
    • INVOKESTATIC:调用静态方法
    • INVOKEVIRTUAL:调用实例方法

6.2 RASP实现挑战

  1. premain vs agentmain

    • premain:启动时加载,可添加方法
    • agentmain:运行时附加,不可添加方法
  2. 类重定义限制

    • 不能添加/删除方法
    • 不能更改父类
    • 不能更改接口
    • 不能更改方法签名
  3. 性能考量

    • 尽量减少transform操作
    • 使用COMPUTE_MAXS自动计算最大栈和局部变量
    • 避免不必要的类转换

7. RASP实践中的思考

7.1 RASP的优势

  1. 上下文感知:拥有完整的执行上下文信息
  2. 0day防护:对未知漏洞有一定防护能力
  3. 低误报率:基于实际行为而非模式匹配
  4. 供应链安全:能监控第三方库的行为

7.2 RASP的局限性

  1. 语言限制:不同语言需要不同实现
  2. 部署复杂性:需要侵入应用
  3. 稳定性风险:错误可能导致应用崩溃
  4. 自身安全:RASP本身可能成为攻击面

7.3 RASP最佳实践

  1. 与WAF/EDR协同:构建纵深防御体系
  2. 规则精细化:针对业务定制规则
  3. 性能优化:减少对业务的影响
  4. 安全加固:确保RASP自身安全

8. 总结

RASP作为一种深度集成的运行时防护技术,能够提供传统边界防护无法实现的精细化安全控制。通过字节码插桩技术,RASP可以在关键API调用点插入安全检查逻辑,实现对命令执行、表达式注入等攻击的有效防护。

实现一个完整的RASP解决方案需要考虑:

  1. 选择合适的hook点
  2. 设计高效的字节码转换逻辑
  3. 构建完善的规则体系
  4. 确保部署和运行的稳定性
  5. 与其他安全产品协同工作

随着应用安全需求的不断提升,RASP将在应用安全防护体系中扮演越来越重要的角色。

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 项目结构 2.2 agent模块配置 pom.xml关键配置 MANIFEST.MF配置 2.3 Tomcat虚拟机选项 3. RASP核心实现 3.1 premain模式基础实现 Agent类 AgentTransform类 3.2 ASM字节码操作 TestClassVisitor实现 增强版AgentTransform 3.3 命令执行拦截实现 ProcessBuilderHook类 增强版TestClassVisitor 4. 表达式注入监测 4.1 表达式注入监测实现 Agent类实现 MethodHookDesc辅助类 5. agentmain动态附加实现 5.1 agentmain核心实现 attachAgent类 CustomClassTransformer类 5.2 动态附加程序 Attachit_ execinRun类 5.3 agentmain的MANIFEST.MF配置 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将在应用安全防护体系中扮演越来越重要的角色。