内容简介:本篇主讲RASP实现(简易版),所有的环境可参考: 浅代码上传GitHub地址:在cn.org.javaweb.agent包下新建一个类。
本篇主讲RASP实现(简易版),所有的环境可参考: 浅 谈RASP技术攻防之实战[环境配置篇]
代码上传GitHub地址: https://github.com/iiiusky/java_rasp_example
前言
关于 ASM 中不同类不同方法之间的关系图如下
简易版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。
由上图可见我们的包的位置为
/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);
}
@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,看一下会输出点什么。
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方法进行更改。具体代码如下:
@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
可以看到已经将当前目录下的内容打印了出来。
我们到idea中看看控制台输出了什么。
可以看到我们输入的命令
[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以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 网络攻防实战演习之蓝队指南
- 攻防演练实战中的若干Tips
- 新书推荐 | 网络攻防实战研究:漏洞利用与提权
- 浅谈RASP技术攻防之实战(环境配置与代码实现)
- 好书推荐 |《Web安全攻防:渗透测试实战指南》(福利见文末!)
- DVWA 黑客攻防实战(十五) 绕过内容安全策略 Content Security Policy (CSP) Bypass
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
经济学原理(第7版):微观经济学分册+宏观经济学分册(套装共2册)
曼昆 (N.Gregory Mankiw) / 梁小民、梁砾 / 北京大学出版社 / 2015-5-1 / 128
《经济学原理(第7版):微观经济学分册+宏观经济学分册》是目前国内市场上最受欢迎的引进版经济学教材之一,其最大特点是它的“学生导向”,它更多地强调经济学原理的应用和政策分析,而非经济学模型。第7版在延续该书一贯风格的同时,对第6版作了全面修订和改进。大幅更新了“新闻摘录”“案例研究”等专栏,拓展了章后习题。一起来看看 《经济学原理(第7版):微观经济学分册+宏观经济学分册(套装共2册)》 这本书的介绍吧!