浅谈RASP技术攻防之实战(环境配置与代码实现)

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

内容简介:之前偶们有就RASP技术攻防基础作过简单介绍,穿越捷径:请点击今儿接上回,说一说环境配置的事儿,废话不多说,直接进入正题:

之前偶们有就RASP技术攻防基础作过简单介绍,穿越捷径:请点击

今儿接上回,说一说环境配置的事儿,废话不多说,直接进入正题:

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

初始化项目

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

取名为JavawebAgent

浅谈RASP技术攻防之实战(环境配置与代码实现) 浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

依然选择Maven项目

浅谈RASP技术攻防之实战(环境配置与代码实现)

然后在ArtifactId处填入agent

浅谈RASP技术攻防之实战(环境配置与代码实现)

然后确定即可

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现) 浅谈RASP技术攻防之实战(环境配置与代码实现)

这时候的目录结构如下

浅谈RASP技术攻防之实战(环境配置与代码实现)

其中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中

浅谈RASP技术攻防之实战(环境配置与代码实现)

≡≡ 创建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 , 然后点击此按钮

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

浅谈RASP技术攻防之实战(环境配置与代码实现)

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

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

cn.org.javaweb.agent

浅谈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的运行机制也会有一定的了解,方便以后调试以及写代码。

参考

https://rasp.baidu.com/doc/hacking/architect/hook.html#java-server

https://github.com/anbai-inc/javaweb-codereview

https://static.javadoc.io/org.ow2.asm/asm/5.2/org/objectweb/asm/ClassReader.html

http://www.blogjava.net/vanadies10/archive/2011/02/23/344899.html

http://www.blogjava.net/DLevin/archive/2014/06/25/414292.html

*本文作者:安百科技,转载请注明来自FreeBuf.COM


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

Numerical Methods and Methods of Approximation in Science and En

Numerical Methods and Methods of Approximation in Science and En

Karan Surana / CRC Press / 2018-10-31

ABOUT THIS BOOK Numerical Methods and Methods of Approximation in Science and Engineering prepares students and other readers for advanced studies involving applied numerical and computational anal......一起来看看 《Numerical Methods and Methods of Approximation in Science and En》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码