freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Java RASP简单实现
2024-11-07 17:21:18
所属地 江苏省

RASP 技术通常内置在一个应用程序或应用程序运行时环境中,能够控制应用程序的执行,并检测漏洞以防止实时攻击,当应用程序开始运行时,RASP 可以通过分析应用程序的行为和该行为的上下文,保护其不受恶意输入或行为的影响。RASP 通过使应用程序持续检测自身的行为,可以立即识别和缓解攻击,且无需人工干预。在Java应用程序中,通常使用Agent来进行实现。

一 前置知识

Agent

agent分为preagent和agentmain,其主要差别为一个是在应用程序启动前进行加载,一个是在启动中也可以进行加载,agentmain的使用更加广泛,本文也主要使用agentmain(了解过agent内存马对这块内容会比较熟悉)

创建一个agent,新建一个类,实现agentmain方法,方法中body可以打印一句话

1730890483_672b4af36c20eb7515e06.png!small?1730890481418

项目中添加一个工件

1730890527_672b4b1f671beac592421.png!small?1730890525201

修改MANIFEST.MF文件,主要添加Agent-Class,指定为我们的类

1730890576_672b4b50784ce21144134.png!small?1730890574399

正常构建工件,即可得到jar包

1730890634_672b4b8a43874ccec75aa.png!small?1730890632011

想要将这个agent注入到别的程序中,我们还需要一个注入器(需要tools.jar的依赖,可手动导入)

代码:

apache.catalina.startup表示我现在注入的是tomcat,这里牵扯到VirtualMachine这个类的使用,可以看我之前agent内存马的文章

import com.sun.tools.attach.*;

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

public class Main {
        public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
                List<VirtualMachineDescriptor> list = VirtualMachine.list();
                for (VirtualMachineDescriptor virtualMachineDescriptor : list) {
                        System.out.println(virtualMachineDescriptor.displayName());
                        if (virtualMachineDescriptor.displayName().contains("apache.catalina.startup")) {
                                VirtualMachine attach = VirtualMachine.attach(virtualMachineDescriptor);
                                attach.loadAgent("路径\\agent01.jar");
                        }

                }
        }
}

当注入成功,会直接执行agentmain中自定义的代码

1730891032_672b4d183a028cc5577ef.png!small?1730891029989

Runtime.getRuntime().exec()本质调用

这里直接看一下,可以发现其实调用的还是java.lang.ProcessImpl的start方法和java.lang.Process

1730891239_672b4de7ecfb2787b034b.png!small?1730891237816

1730891295_672b4e1f9ba0ac4886d0f.png!small?1730891293382

贴一个命令执行流程的图

1730903083_672b7c2b0135f33d85b77.png!small?1730903080977

二 RASP实现

命令执行hook

继续修改agentmain代码,这里涉及 Instrumentation 和javassist(修改字节码)类,

为什么不直接hook Runtime.getRuntime.exec的原因是冰蝎执行命令这里我看到不会通过Runtime方法(如果真实常见应该是全部hook掉)

版本1,只能加载一次,之后不会再触发,通过instrumentation.addTransformer向 JVM 注册一个ClassFileTransformer实例

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Arrays;

public class AgentDemo implements ClassFileTransformer {
    private static final String TARGET_CLASS_NAME = "java.lang.ProcessImpl";

    public static void agentmain(String agentArgs, Instrumentation instrumentation) {
        instrumentation.addTransformer(new AgentDemo(), true);
    }


@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
System.out.println(className.replace('/', '.'));
if (!TARGET_CLASS_NAME.equals(className.replace('/', '.'))) {
return classfileBuffer; // 不是目标类,直接返回
}

try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.getCtClass(TARGET_CLASS_NAME);

CtMethod execMethod = ctClass.getDeclaredMethod("start");
CtMethod[] methods = ctClass.getDeclaredMethods();
String insertBefore = "System.out.println(\"ProcessImpl start method is called\");";
execMethod.insertBefore(insertBefore);

return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
return classfileBuffer; // 发生异常,返回原始字节码
}
}


版本2

每次遇到目标hook类都会重加载,通过instrumentation.retransformClasses实现

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import java.util.Arrays;

public class AgentDemo implements ClassFileTransformer {
    private static final String TARGET_CLASS_NAME = "java.lang.ProcessImpl";

    public static void agentmain(String agentArgs, Instrumentation instrumentation) throws UnmodifiableClassException {
        // 获取所有已加载的类
        Class[] allLoadedClasses = instrumentation.getAllLoadedClasses();
        for (Class<?> clazz : allLoadedClasses) {
            if (clazz.getName().equals(TARGET_CLASS_NAME)) {
                // 对已加载的类应用变换
                instrumentation.retransformClasses(clazz);
            }
        }
        // 添加变换器以应用于之后加载的类
        instrumentation.addTransformer(new AgentDemo(), true);
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        System.out.println(className.replace('/', '.'));
        if (!TARGET_CLASS_NAME.equals(className.replace('/', '.'))) {
            return classfileBuffer; // 不是目标类,直接返回
        }

        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.getCtClass(TARGET_CLASS_NAME);

            CtMethod execMethod = ctClass.getDeclaredMethod("start");
            CtMethod[] methods = ctClass.getDeclaredMethods();
            String insertBefore = "System.out.println(\"ProcessImpl start method is called\");";
            execMethod.insertBefore(insertBefore);

            return ctClass.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classfileBuffer; // 发生异常,返回原始字节码
    }
}

好的,现在启动一个tomcat,注射器加载一下agent,可以看到当执行命令前被检测到,先执行了我们通过javassist写的内容

1730902285_672b790d6bdf9aa256a63.png!small?1730902283213

这就是RASP的基本原理,我们可以自定义检测Hook的函数,如果是恶意操作直接关闭该访问

文件读写Hook

其实只是更换一个hook的类

其实java中对文件操作类有特别多java.io.File、java.io.FileInputStream、java.io.FileOutputStream,还有很多,以java.io.File为例:

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import java.util.Arrays;

import javassist.*;

public class AgentDemo implements ClassFileTransformer {
    private static final String TARGET_CLASS_NAME = "java.io.File";

    public static void agentmain(String agentArgs, Instrumentation instrumentation) throws UnmodifiableClassException {
        // 获取所有已加载的类
        Class[] allLoadedClasses = instrumentation.getAllLoadedClasses();
        for (Class<?> clazz : allLoadedClasses) {
            if (clazz.getName().equals(TARGET_CLASS_NAME)) {
                // 对已加载的类应用变换
                instrumentation.retransformClasses(clazz);
            }
        }
        // 添加变换器以应用于之后加载的类
        instrumentation.addTransformer(new AgentDemo(), true);
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        if (className.equals(TARGET_CLASS_NAME.replace('.', '/'))) {
            try {
                ClassPool pool = ClassPool.getDefault();
                CtClass ctClass = pool.getCtClass(TARGET_CLASS_NAME);

                // 获取 createNewFile 方法
                CtMethod createNewFileMethod = ctClass.getDeclaredMethod("createNewFile");

                // 在 createNewFile 方法前后插入代码
                String beforeCode = "System.out.println(\"Before createNewFile: \";)";
                createNewFileMethod.insertBefore(beforeCode);
                String afterCode = "System.out.println(\"After createNewFile:  \");";
                createNewFileMethod.insertAfter(afterCode);

                return ctClass.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return classfileBuffer; // 发生异常或不是目标类,返回原始字节码
    }

}

更多的不在展示,大致思路就是hook不同的类,可以实现的效果

  1. 文件系统防御(目录遍历、文件读、写、重命名、移动等)
  2. SQL查询防御
  3. XML实体注入防御
  4. 恶意表达式执行防御(Ognl、SpEL、MVEL2等)
  5. 恶意WebShell请求拦截
  6. 恶意文件上传
  7. 本地命令执行
  8. 反序列化攻击(Java、XML、Json)
  9. SSRF攻击

三 绕过思路

现在绕过思路基本就是两个

  • 寻找没有被限制的类或者函数来绕过(类似绕过黑名单)

这个其实就是上面File读写的问题,尽量覆盖所有的或者hook更加深入底层的东西

  • 利用更底层的技术进行绕过,比如上面图中直接hook c代码(难度较大)
  • JNI 绕过 RASP(通过编译so和dll文件的方式)
  • 创建新线程的方式(如果有黑名单无法绕过,但是可以绕过堆栈)
# 渗透测试 # 网络安全 # web安全 # 系统安全 # 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录