关于OpenRASP的功能二次开发

官方文档:
插件开发
二次开发
参考博客:
深入源码对OpenRASP进行原理和设计思想的剖析
JVM 源码分析之 javaagent 原理完全解读
OpenRASP技术分析

1. 原理

简要说明

java 版本使用 javaagent 机制来实现。在服务器启动时,可动态的修改Java字节码,对敏感操作的函数进行挂钩,比如:

  • 数据库操作
  • 文件读取、写入操作
  • 命令执行

  • 当服务器发生攻击,就会触发这些Hook点,此时RASP agent就可以获取到函数的参数,比如要读取的文件名、要执行的命令等等。

启动流程

  1. 启动时首先会进入 javaagent 的 premain 函数,该函数会在 main 函数之前预先执行;
  2. 当去 hook 像 java.io.File 这样由 BootstrapClassLoader 加载的类的时候,无法从该类调用非 BootstrapClassLoader 加载的类中的接口,所以 agent.jar 会先将自己添加到 BootstrapClassLoader 的ClassPath下,这样 hook 由 BootstrapClassLoader 加载的类的时候就能够成功调用到 agent.jar 中的检测入口;
  3. 释放 log4j 日志配置文件,如果存在则跳过
  4. 根据 openrasp.yml 文件初始化相应配置项
  5. 初始化 JS 插件模块
    • JS 上下文类初始化
    • 插件文件初始化
  6. 初始化字节码转换模块
    • 给 load class 操作进行插桩操作,当类加载的时候会先进入 agent 进行处理
    • 对于在初始化前已加载的类执行 retransform 处理,e.g FileInputStream
  7. 输出启动成功日志,开启全局 Hook 开关(启动阶段为关闭状态)
    • 若启动过程中发生错误,记录错误日志
  8. 给 openrasp.yml 配置文件和 js 插件目录以及 assets 目录增加文件监控,以便文件内容更改的时候不需要重启就能够实时生效

Hook Class 流程

  1. 因为启动时候进行了插桩操作,当有类被 ClassLoader 加载时候,所以会把该类的字节码先交给自定义的 Transformer 处理
  2. 自定义 Transformer 会判断该类是否为需要 hook 的类,如果是会将该类交给 javassist 字节码处理框架进行处理,javassist
  3. javassist 框架会将类的字节码依照事件驱动模型逐步解析每个方法,当触发了我们需要 hook 的方法,我们会在方法的开头或者结尾插入进入检测函数的字节码
  4. 把 hook 好的字节码返回给 transformer 从而载入虚拟机
    启动时架构如下图所示:
    在这里插入图片描述

请求处理流程

我们以 tomcat + JDBC + MySQL 为例,简单说明下请求处理的流程

  1. 服务器收到一个请求,从而进入了服务器的请求 hook 点(该 hook 点每个服务器不一样,具体参照源码),该 hook 点标注当前线程为请求线程,开启当前线程的检测开关并把请求对象和响应对象进行缓存,以便后面使用
  2. 服务器发起SQL查询
  3. 进入 SQLStatementHook 点,我们挂钩了 execute、executeUpdate、executeQuery 等方法,从该方法进入检测流程如下:
    • 判断当前线程是否为请求线程(第一步标记的),如果是继续下面检测
    • 采集 connection_id(这个字段仅JDBC支持)、SQL 语句以及数据库类型 等信息
    • 构建参数信息,调用本地插件和 JS 插件进行安全检测,JS 插件由 Rhino 引擎执行,Rhino 引擎执行是 mozilla 为 java 提供JavaScript引擎,该引擎会将 JS 代码编译为 java 的 class 字节码在 JVM 中运行
    • 根据插件的执行结果决定是拦截请求、放行还是仅打印日志
  4. 进入 SQLResultSetHook 点,我们挂钩了 resultSet.next 方法
    • 调用本地插件检查是否发生拖库行为,默认策略为一次查询结果超过500条就报警
  5. 若决定拦截攻击
    • 输出报警日志到 logs/alarm.log
    • 如果header还没有发出,默认使用 302 跳转到拦截页面
    • 如果body还没有发出,则重置未发送的body
    • 输出自定义拦截页面跳转js脚本
      </script><script>location.href='.../?request_id=xxx'</script>
      OpenRASP 中JS引擎执行架构如下图所示:
      在这里插入图片描述

2. 实践

以【009 - 使用 InvokerTransformer 反序列化并执行命令】为例

java-agent项目

1. 初始化,新增DeserializationHook类

重写getType方法:新增Hook类型,和CheckParameter.Type里的枚举保持一致
重写isClassMatched方法:新增Hook对应匹配拦截的类,这里拦截的是java.io.ObjectInputStream类型
在这里插入图片描述
初始化hoodMethod方法,该 hookMethod 方法在项目启动时利用 Javassist 修改了反序列化过程中的 resolveClass 方法,在其开始之前插入了一个静态方法调用 (checkDeserializationClass) 来检查反序列化类是否安全。这样可以通过运行时检查防御反序列化攻击,防止加载和执行不安全的类。
在这里插入图片描述
checkDeserializationClass方法:在特定请求请求下,isClassMatched=true时触发hookMethod方法,通过hookMethod方法进入checkDeserializationClass方法最终进入HookHandler的doCheck方法,最终执行js插件代码进行判断。
在这里插入图片描述

2. 在JS类里新增插件类型

在com.baidu.openrasp.plugin.js.JS类的pluginConfig中,追加新增的插件类型,这里是deserialization。(注:official.js要和这个保持一致,不然会导致加载不到插件而调用超时)
在这里插入图片描述注意:这个不能漏,漏了调用JS代码的时候会有超时问题

3. 打包agent并进行更新替换

将你本地项目打包后target下的rasp.jar、rasp-engine.jar替换Tomcat下rasp目录下的rasp.jar、rasp-engine.jar即可。
在这里插入图片描述
在这里插入图片描述

official.js文件

1. 注册新插件

  1. 在if (algorithmConfig.meta.log_event) {} 里面新增插件,用于打印日志。
    在这里插入图片描述
  2. 在全局根路径的新增插件,该插件对应拦截的逻辑
    在这里插入图片描述
  3. 该插件的初始化配置行为
    name:插件的名称;
    action:插件的初始化行为,包括igonre(忽略)、log(打印日志),block(拦截);(这里只是初始化,可在管理后台进行热修改)
    clazz_blacklist: 反序列化类的黑名单列表配置,内容是Java的全路径类名;(这里只是初始化,可在管理后台进行热修改)
    在这里插入图片描述

2. 上传插件并配置生效

  1. 开发完成后,上传最新的js插件,然后推送,推送成功后进行配置
  2. 这里需要将这个按钮关闭,不然所有的请求都不会拦截

在这里插入图片描述

3. 总结

经过这轮的开发,基本可以掌握OpenRASP插件的二次开发了,以反序列化插件作为入门场景是现在浅显易懂的,不过也不要过度乐观的认为OpenRASP二开很简单,因为后续会有一些比较深入的场景,当前的开发模式不一定满足,本人也是入门小白一枚,欢迎各位同行留言交流进步!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值