PS:代码已上传至github,地址:https://github.com/iiiusky/java_rasp_example

初始化项目

首先我们在IDEA中新建一个maven项目

取名为JavawebAgent

然后当前的目录结构如下:

删除src目录,然后右键新建Module

依然选择Maven项目

然后在ArtifactId处填入agent

然后确定即可

然后再次重复一遍上面的新建Module的操作,将第二小步中的ArtifactId改为test,第三小步中的Module Name 改为test-struts2,如下图所示

这时候的目录结构如下

其中agent目录为我们要实现agent的主要代码区域,test-struts2为测试web代码区域。(注:test-struts2不是必选的)

test-struts2模块基础配置

test-struts2部分的代码这边就不进行复述了,大家可以去本项目的地址中直接下载test-struts2内容。

agent模块基本配置

≡≡ pom.xml包配置

agent这个pom包配置的话有坑,这个以后在说,先看pom.xml内容吧。

<dependencies><dependency><groupId>org.ow2.asm</groupId><artifactId>asm-all</artifactId><version>5.1</version></dependency></dependencies><build><finalName>agent</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.6</source><target>1.6</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>2.3.2</version><configuration><archive><manifestFile>src/main/resources/MANIFEST.MF</manifestFile></archive></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>2.3</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><artifactSet><includes><include>commons-io:commons-io:jar:*</include><include>org.ow2.asm:asm-all:jar:*</include></includes></artifactSet></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.21.0</version><configuration><skipTests>true</skipTests></configuration></plugin></plugins></build>

将上述内容复制到agent模块下的pom.xml中

≡≡ 创建MAINFEST.NF文件

在resources目录下创建MAINFEST.NF文件,文件内容如下

Manifest-Version: 1.0
Can-Retransform-Classes: true
Can-Redefine-Classes: true
Can-Set-Native-Method-Prefix: true

≡≡ maven自动打包配置

在idea中右上部分找到Add Configurations , 然后点击此按钮

在弹出的窗口中点左上角的+,选择maven

然后点下图①的位置选择工作目录,在②的位置选择agent,在③的位置填入clean install

完成以后如下图所示,然后点击OK保存即可

这时候右上角已经可以看到我们刚刚配置的maven自动打包功能了,agent每改一处都需要重新build,不然无法生效。

≡≡ 创建Agent主要实现代码包

在agent包下面的java文件夹下右键选择新建package,然后填入你的包名,我这边的包名为

cn.org.javaweb.agent

简易版RASP实现

创建入口类

在cn.org.javaweb.agent包下新建一个类。

内容如下:

/** Copyright sky 2019-04-03 Email:sky@03sec.com.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package cn.org.javaweb.agent;
import java.lang.instrument.Instrumentation;
/*** @author sky*/
public class Agent {public static void premain(String agentArgs, Instrumentation inst) {inst.addTransformer(new AgentTransform());}
}

创建Transform

然后我们再新建一个AgentTransform类,该类需要实现ClassFileTransformer的方法,内容如下:

/** Copyright sky 2019-04-03 Email:sky@03sec.com.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package cn.org.javaweb.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
/*** @author sky*/
public class AgentTransform implements ClassFileTransformer {/*** @param loader* @param className* @param classBeingRedefined* @param protectionDomain* @param classfileBuffer* @return* @throws IllegalClassFormatException*/@Overridepublic byte[] transform(ClassLoader loader, String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {className = className.replace("/", ".");System.out.println("Load class:" + className);return classfileBuffer;}
}

build Agent配置

点击右上角的agent[clean,intall]进行build。

由上图可见我们的包的位置为

/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar

将改包的位置记录下来,然后点开tomcat配置(这边没有对idea如何配置tomcat进行讲解,不会的可以自行百度|谷歌)

在VM options处填写以下内容:

-Dfile.encoding=UTF-8
-noverify
-Xbootclasspath/p:/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar
-javaagent:/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar

其中/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar的路径为你在上一步编译出来的agent的路径,注意替换。

这时候我们在启动tomcat,就可以看到我们在AgentTransform中写的打印包名已经生效了,如下图:

上图红框区域为tomcat启动的时候加载的所有类名。然后我们打开浏览器查看web是否正常。

可以看到web也正常启动了。

创建ClassVisitor类

然后我们新建一个TestClassVisitor类,需要继承ClassVisitor类并且实现Opcodes类,代码如下

/** Copyright sky 2019-04-03 Email:sky@03sec.com.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package cn.org.javaweb.agent;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/*** @author sky*/
public class TestClassVisitor extends ClassVisitor implements Opcodes {public TestClassVisitor(ClassVisitor cv) {super(Opcodes.ASM5, cv);}@Overridepublic 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;}
}

对ProcessBuilder(命令执行)类进行hook用户执行的命令

≡≡ 使用transform对类名进行过滤

然后回到AgentTransform中,对transform方法的内容进行修改,transform方法代码如下:

public byte[] transform(ClassLoader loader, String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {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;}

简单介绍一下代码块内容

首先判断类名是否包含ProcessBuilder,如果包含则使用ClassReader对字节码进行读取,然后新建一个ClassWriter进行对ClassReader读取的字节码进行拼接,然后在新建一个我们自定义的ClassVisitor对类的触发事件进行hook,在然后调用classReader的accept方法,最后给classfileBuffer重新赋值修改后的字节码。

可能看起来比较绕,但是如果学会使用以后就比较好理解了。

≡≡ 创建测试环境

我们在tomcat中新建一个jsp,用来调用命令执行,代码如下:

<%@ page import="java.io.InputStream" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<pre>
<%Process process = Runtime.getRuntime().exec(request.getParameter("cmd"));InputStream in = process.getInputStream();int a = 0;byte[] b = new byte[1024];while ((a = in.read(b)) != -1) {out.println(new String(b, 0, a));}in.close();
%>
</pre>

可以看到就是一个简单的执行命令的代码;下面我们对就此更改过的内容进行build,看一下会输出点什么。

biuld完成,启动tomcat。

访问

http://localhost:8080/cmd.jsp?cmd=whoami

可以看到已经成功执行命令,我们回到idea里面的控制台看一下输出了什么。

通过上图可以完整的看到一个执行命令所调用的所有调用链。

Load class: java.lang.ProcessBuilder
<init>方法的描述符是:(Ljava/util/List;)V
<init>方法的描述符是:([Ljava/lang/String;)V
command方法的描述符是:(Ljava/util/List;)Ljava/lang/ProcessBuilder;
command方法的描述符是:([Ljava/lang/String;)Ljava/lang/ProcessBuilder;
command方法的描述符是:()Ljava/util/List;
environment方法的描述符是:()Ljava/util/Map;
environment方法的描述符是:([Ljava/lang/String;)Ljava/lang/ProcessBuilder;
directory方法的描述符是:()Ljava/io/File;
directory方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirects方法的描述符是:()[Ljava/lang/ProcessBuilder$Redirect;
redirectInput方法的描述符是:(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;
redirectOutput方法的描述符是:(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;
redirectError方法的描述符是:(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;
redirectInput方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirectOutput方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirectError方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirectInput方法的描述符是:()Ljava/lang/ProcessBuilder$Redirect;
redirectOutput方法的描述符是:()Ljava/lang/ProcessBuilder$Redirect;
redirectError方法的描述符是:()Ljava/lang/ProcessBuilder$Redirect;
inheritIO方法的描述符是:()Ljava/lang/ProcessBuilder;
redirectErrorStream方法的描述符是:()Z
redirectErrorStream方法的描述符是:(Z)Ljava/lang/ProcessBuilder;
start方法的描述符是:()Ljava/lang/Process;
<clinit>方法的描述符是:()V
Load class: java.lang.ProcessBuilder$NullInputStream
<init>方法的描述符是:()V
read方法的描述符是:()I
available方法的描述符是:()I
<clinit>方法的描述符是:()V
Load class: java.lang.ProcessBuilder$NullOutputStream
<init>方法的描述符是:()V
write方法的描述符是:(I)V
<clinit>方法的描述符是:()V

≡≡ 拿到用户所执行的命令

接下来我们看看尝试一下能否拿到所执行的命令

新建一个名为ProcessBuilderHook的类,然后在类中新建一个名字为start的静态方法,完整代码如下:

/** Copyright sky 2019-04-04 Email:sky@03sec.com.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package cn.org.javaweb.agent;
import java.util.Arrays;
import java.util.List;
/*** @author sky*/
public class ProcessBuilderHook {public static void start(List<String> commands) {String[] commandArr = commands.toArray(new String[commands.size()]);System.out.println(Arrays.toString(commandArr));}
}

这个方法干啥用的我们一会在说,先看下面。

≡≡ 复写visitMethod方法

打开TestClassVisitor,对visitMethod方法进行更改。具体代码如下:

@Overridepublic 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)) {System.out.println(name + "方法的描述符是:" + desc);return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {@Overridepublic 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;}

给大家解释下新增加的代码,从if判断开始

判断传入进来的方法名是否为start以及方法描述符是否为()Ljava/lang/Process;,如果是的话就新建一个AdviceAdapter方法,并且复写visitCode方法,对其字节码进行修改,

mv.visitVarInsn(ALOAD, 0);

拿到栈顶上的this

mv.visitFieldInsn(GETFIELD, "java/lang/ProcessBuilder", "command", "Ljava/util/List;");

拿到this里面的command

mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/agent/ProcessBuilderHook", "start", "(Ljava/util/List;)V", false);

然后调用我们上面新建的ProcessBuilderHook类中的start方法,将上面拿到的this.command压入我们方法。

ProcessBuilderHook类的作用就是让这部分进行调用,然后转移就可以转入到我们的逻辑代码了。

我们再次编译一下,然后启动tomcat,访问cmd.jsp看看.

≡≡ 测试hook用户执行的命令参数是否拿到

访问

http://localhost:8080/cmd.jsp?cmd=ls -la

可以看到已经将当前目录下的内容打印了出来。

我们到idea中看看控制台输出了什么。

可以看到我们输入的命令

[whoami]

已经输出出来了,到此为止,我们拿到了要执行的命令.

总结

对于拿到要执行的命令以后怎么做,是需要拦截还是替换还是告警,这边就需要大家自己去实现了。当然,如果要实现拦截功能,还需要注意要获取当前请求中的的response,不然无法对response进行复写,也无法对其进行拦截。这边给大家提供一个思路,对应拦截功能,大家可以去hook请求相关的类,然后在危险hook点结合http请求上下文进行拦截请求。

对于其他攻击点的拦截,可以参考百度开源的OpenRasp进行编写hook点。

浅谈RASP技术攻防之实战相关推荐

  1. rasp 系统_浅谈RASP技术攻防之基础篇

    引言 本文就笔者研究RASP的过程进行了一些概述,技术干货略少,偏向于普及RASP技术.中间对java如何实现rasp技术进行了简单的举例,想对大家起到抛砖引玉的作用,可以让大家更好的了解一些关于we ...

  2. 「系统安全」浅谈RASP技术攻防

    一 .什么是RASP? 在2014年的时候,Gartner引入了"Runtime application self-protection"一词,简称为RASP.它是一种新型应用安全 ...

  3. 浅谈.NET技术公司的实习生培养

    浅谈.NET技术公司的实习生培养 背景 近几年.NET开发者市场的越发不景气,一毕业就选择.NET技术的开发者更是少之又少.一方面是公司效益的日益提高,一方面却是招聘优秀人才的速度总是赶不上公司发展的 ...

  4. 浅谈Hybrid技术的设计与实现第二弹

    前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术的设计与实现第三弹--落地篇 接上文:浅谈Hybrid技术的设计与实现(阅读本文前,建议阅读这个先) ...

  5. 浅谈云上攻防 --SSRF 漏洞带来的新威胁

    前言 在<浅谈云上攻防--元数据服务带来的安全挑战>一文中,生动形象的为我们讲述了元数据服务所面临的一系列安全问题,而其中的问题之一就是通过SSRF去攻击元数据服务:文中列举了2019年美 ...

  6. 浅谈虚拟化技术下的云安全如何处置

    浅谈虚拟化技术下的云安全如何处置 近年来,云计算是目前非常热门的一个研究领域,其实它并不是一种全新的技术,而是许多技术的融合体,包括分布式计算.动态和拓展等各种各样的技术算法,而虚拟化技术是云计算里最 ...

  7. 浅谈Hybrid技术的设计与实现【转】

    https://www.cnblogs.com/yexiaochai/p/4921635.html 前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术 ...

  8. 智能车浅谈——抗干扰技术硬件篇

    文章目录 前言 干扰 什么是干扰 干扰窜入的主要途径 干扰的分类 硬件抗干扰技术 控制系统的电源保护技术 输入/输出传输线的抗干扰措施 I/O接口的抗干扰措施 接地技术 总结 智能车系列文章汇总 前言 ...

  9. 智能车浅谈——抗干扰技术软件篇

    文章目录 软件抗干扰技术 数字信号的抗干扰措施 数字输入信号软件抗干扰措施 数字输出信号软件抗干扰措施 数字滤波 算术平均值滤波 中值滤波 滑动平均滤波 归一化 差比和 CPU及程序的抗干扰措施 复位 ...

最新文章

  1. 前端的单页面模式和多页面模式
  2. python3 django配置数据库(mysql)
  3. python重命名文件或目录_Python重命名多个文件的实例方法
  4. ambari集成mysql_Ambari 安装配置 MySql
  5. 全球计算机科学硕士申请,2019爱尔兰留学都柏林大学计算机科学硕士申请
  6. 第5章 Python 数字图像处理(DIP) - 图像复原与重建5 - 均匀噪声
  7. [Java基础]字符流读写数据的方式
  8. 数据结构06树和二叉树
  9. heart beat 安装与配置
  10. Solidworks如何在自定义的基准面上创建3D草图
  11. Python抖音机器人制作!让你有看不完的小姐姐
  12. oracle sql developer 执行sql语句,如何使用SQL Developer执行SQL语句(上)
  13. 修改vscode代码字体大小
  14. vim配置:高亮所在的行和列
  15. SEO优化之alt属性和title属性
  16. python123随机密码生成_Python生成对应随机密码文件
  17. php生成拟合线,excel拟合曲线怎么做
  18. github releases一直失败的解决方案
  19. Softmax回归求导公式推导
  20. Tp5.0对接腾讯云语音验证码

热门文章

  1. 深度学习中处理类别不平衡问题主流方法总结
  2. 题8.13:写一个用矩形法求定积分的通用函数,分别求$\int_0^1 sinxdx, \quad \int_0^1 cosxdx, \quad \int_0^1 e^xdx,$
  3. 5分钟带你了解Python中的容器型数据类型--列表1
  4. 机器学习中的Encoder和Decoder到底是什么
  5. 开发3D游戏建模都需要哪些软件?软件繁多,如何从中挑选学习?
  6. 【学习记录】Tpro遥控器_暂时取消Tpro的控制权(简易)
  7. 指纹识别综述(11): 专利
  8. win10代理服务器没有响应解决方案
  9. idea 回退merge_详解IDEA git分支回退指定的历史版本
  10. 【四足机器人那些事】足端轨迹规划(一)