2.1Java Instrumentation
ljar包的MANIFEST.MF文件必须指定Premain-Class
lPremain-Class指定的类必须实现premain()方法。
使用javaagent需要几个步骤:
l定义一个MANIFEST.MF文件,必须包含Premain-Class选项,也需要加入Can-Redefine-Classes和Can-Retransform-Classes选项
l创建Premain-Class指定的类,类中包含premain函数,该方法可以进一步加载RASP实现原理
l将MANIFEST.MF和写好的各种类打包成jar
l启动java时,添加-javaagent:xx.jar,即可让java先自动执行写好的premain方法
package com.example; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; public class PreMain { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("agentArgs:" + agentArgs); inst.addTransformer(new DefineTransformer(), true); } static class DefineTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("premain load Class: " + className); // 注意这里的输出 return classfileBuffer; } } } |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifestEntries> <Premain-Class>com.example.PreMain</Premain-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build> |
package com.example; public class Main { public static void main(String[] args) { System.out.println("Main.main() in test project"); } } |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifestEntries> <Main-Class>com.example.Main</Main-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build> |
java -javaagent:MyAgent.jar -jar MyRaspDemoMain-1.0-SNAPSHOT.jar |
public class Agent { public static void premain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException { Console.log("init"); init(); inst.addTransformer(new ClassTransformer()); } private static boolean init() { Config.initConfig(); return true; } } |
public class Config { public static Map<String, Map<String, Object>> moudleMap = new ConcurrentHashMap<String, Map<String, Object>>(); @SuppressWarnings({ "rawtypes", "unchecked" }) public static boolean initConfig() { String configStr = readConfig("/main.config"); if (configStr == null) { Console.log("init failed because of config file error"); return false; } Map configMap = (Map) JSONUtils.parse(configStr); List<Map> moudleList = (List<Map>) configMap.get("moudle"); for (Map m: moudleList) { Map<String, Object> tmpMap = new ConcurrentHashMap<String, Object>(); tmpMap.put("loadClass", m.get("loadClass")); tmpMap.put("mode", m.get("mode")); tmpMap.put("whiteList", new CopyOnWriteArrayList<String>((Collection) m.get("whiteList"))); tmpMap.put("blackList", new CopyOnWriteArrayList<String>((Collection) m.get("blackList"))); moudleMap.put((String)m.get("moudleName"), tmpMap); } Console.log(moudleMap.toString()); return true; } ... } |
{ "moudle": [ { "moudleName": "java/lang/ProcessBuilder", "loadClass": "xbear.javaopenrasp.visitors.rce.ProcessBuilderVisitor", "mode": "log", "whiteList":[], "blackList": [ "calc", "etc", "var", "opt", "apache", "bin", "passwd", "login", "cshrc", "profile", "ifconfig", "tcpdump", "chmod", "cron", "sudo", "su", "rm", "wget", "sz", "kill", "apt-get", "find" ] }, { "moudleName": "java/io/ObjectInputStream", "loadClass": "xbear.javaopenrasp.visitors.rce.DeserializationVisitor", "mode": "black", "whiteList":[], "blackList": [ "org.apache.commons.collections.functors.InvokerTransformer", "org.apache.commons.collections.functors.InstantiateTransformer", "org.apache.commons.collections4.functors.InvokerTransformer", "org.apache.commons.collections4.functors.InstantiateTransformer", "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.beans.factory.ObjectFactory" ] }, ... |
public class ClassTransformer implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { byte[] transformeredByteCode = classfileBuffer; if (Config.moudleMap.containsKey(className)) { try { ClassReader reader = new ClassReader(classfileBuffer); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor visitor = Reflections.createVisitorIns((String) Config.moudleMap.get(className).get("loadClass"), writer, className); reader.accept(visitor, ClassReader.EXPAND_FRAMES); transformeredByteCode = writer.toByteArray(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } return transformeredByteCode; } } |
1. ClassReader:按照Java虚拟机规范中定义的方式来解析class文件中的内容,在遇到合适的字段时调用ClassVisitor中相对应的方法。
2. ClassVisitor:java中类的访问者,提供一系列方法由ClassReader调用。是一个抽象类,我们在使用的时候需要继承此类。使用此对象的时候需要指定asm api的版本。
3. ModuleVisitor:Java中模块的访问者,作为ClassVisitor.visitModule方法的返回值,要是不关心模块的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
4. AnnotationVisitor:Java中注解的访问者,作为ClassVisito中visitTypeAnnotation和visitTypeAnnotation的返回值,要是不关心注解的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
5. FieldVisitor:Java中字段的访问者,作为ClassVisito.visitField的返回值,要是不关心字段的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
6. MethodVisitor:Java中方法的访问者,作为ClassVisito.visitMethod的返回值,要是不关心方法的使用情况,可以返回一个null。使用此对象的时候需要指定asm api的版本。
而xbear.javaopenrasp.visitors.rce.DeserializationVisitor源码如下:
public class DeserializationVisitor extends ClassVisitor { public String className; public DeserializationVisitor(ClassVisitor cv, String className) { super(Opcodes.ASM5, cv); this.className = className; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if ("resolveClass".equals(name) && "(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;".equals(desc)) { mv = new DeserializationVisitorAdapter(mv, access, name, desc); } return mv; } } |
public class DeserializationVisitorAdapter extends AdviceAdapter { public DeserializationVisitorAdapter(MethodVisitor mv, int access, String name, String desc) { super(Opcodes.ASM5, mv, access, name, desc); } @Override protected void onMethodEnter() { mv.visitTypeInsn(NEW, "xbear/javaopenrasp/filters/rce/DeserializationFilter"); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "xbear/javaopenrasp/filters/rce/DeserializationFilter", "<init>", "()V", false); mv.visitVarInsn(ASTORE, 2); mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, "xbear/javaopenrasp/filters/rce/DeserializationFilterr", "filter", "(Ljava/lang/Object;)Z", false); Label l92 = new Label(); mv.visitJumpInsn(IFNE, l92); mv.visitTypeInsn(NEW, "java/io/IOException"); mv.visitInsn(DUP); mv.visitLdcInsn("invalid class in deserialization because of security"); mv.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "(Ljava/lang/String;)V", false); mv.visitInsn(ATHROW); mv.visitLabel(l92); } |
public class DeserializationFilter implements SecurityFilterI { @Override public boolean filter(Object forCheck) { String moudleName = "java/io/ObjectInputStream"; ObjectStreamClass desc = (ObjectStreamClass) forCheck; String className = desc.getName(); String mode = (String) Config.moudleMap.get(moudleName).get("mode"); switch (mode) { case "block": Console.log("block: " + className); return false; case "white": if (Config.isWhite(moudleName, className)) { Console.log("pass: " + className); return true; } Console.log("block:" + className); return false; case "black": if (Config.isBlack(moudleName, className)) { Console.log("block: " + className); return false; } Console.log("pass: " + className); return true; case "log": default: Console.log("pass: " + className); Console.log("log stack trace:rn" + StackTrace.getStackTrace()); return true; } } } |
String rememberMe = cookie.getValue(); byte[] decoded = Base64.getDecoder().decode(rememberMe); ByteArrayInputStream bytes = new ByteArrayInputStream(decoded); ObjectInputStream in = new ObjectInputStream(bytes); in.readObject(); in.close(); |
https://blog.csdn.net/hy1273383167/article/details/116211211
https://github.com/xbeark/javaopenrasp
原文始发于微信公众号(SAINTSEC):java运行时应用自保护RASP技术浅析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论