浅谈RASP技术攻防之实战[代码实现篇]

栏目: Java · 发布时间: 5年前

内容简介:本篇主讲RASP实现(简易版),所有的环境可参考: 浅代码上传GitHub地址:在cn.org.javaweb.agent包下新建一个类。

本篇主讲RASP实现(简易版),所有的环境可参考: 浅 谈RASP技术攻防之实战[环境配置篇]

代码上传GitHub地址: https://github.com/iiiusky/java_rasp_example

前言

关于 ASM 中不同类不同方法之间的关系图如下

浅谈RASP技术攻防之实战[代码实现篇]

简易版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
*/
@Override
public 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。

浅谈RASP技术攻防之实战[代码实现篇]

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

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

浅谈RASP技术攻防之实战[代码实现篇]

在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中写的打印包名已经生效了,如下图:

浅谈RASP技术攻防之实战[代码实现篇]

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

浅谈RASP技术攻防之实战[代码实现篇] 可以看到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);
}

@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;
}
}

对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,看一下会输出点什么。

浅谈RASP技术攻防之实战[代码实现篇]

biuld完成,启动tomcat。

浅谈RASP技术攻防之实战[代码实现篇]

访问

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

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

浅谈RASP技术攻防之实战[代码实现篇]

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

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方法进行更改。具体代码如下:

@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)) {
System.out.println(name + "方法的描述符是:" + 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;
}

给大家解释下新增加的代码,从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%20-la

浅谈RASP技术攻防之实战[代码实现篇]

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

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

浅谈RASP技术攻防之实战[代码实现篇]

可以看到我们输入的命令

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

总结

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

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

如需在 Java 中实现RASP技术,笔者建议好好了解一下ASM,这样对以后JAVA的运行机制也会有一定的了解,方便以后调试以及写代码。

参考


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Everything Store

The Everything Store

Brad Stone / Little, Brown and Company / 2013-10-22 / USD 28.00

The definitive story of Amazon.com, one of the most successful companies in the world, and of its driven, brilliant founder, Jeff Bezos. Amazon.com started off delivering books through the mail. Bu......一起来看看 《The Everything Store》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具