JDK7U21调用链

Posted by 跳跳龙 on March 15, 2020

JDK7u21

前面我们说的调用链基本都是围绕的是Java第三方包中的一些内容,但是随着JDK7U21的出现,彻彻底底的说明了反序列化根本不需要使用第三方库的支持。而且JDK7u21的利用过程十分精彩,使用HashSet,TemplatesImpl和AnnoationInvocationHandler,其中后两个前面的Collections调用链中基本都提到过。关于HashSet这个容器,实际上就是底层使用了Hash表来实现的,它不允许元素重复,但它不保证顺序,这是和List的最大区别。

这一篇的文章中,我暂时不准备画出整个调用链的调用关系。后面在单独更新一下他的调用链图,这里主要分析调用细节。

这里面的代码我是直接使用ysoserial项目生成的,直接输出到jdk7u21.cer文件中,然后进行反序列化。

java -jar ysoserial.jar Jdk7u21 "open /Applications/Calculator.app" > jdk7u21.cer

注意我这里的JDK版本。

❱ ./java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b12)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

当进行readObject操作的时候,发现他是一个HashSet,会直接调用HashSet的ReadObject。

我们发现在内部实现中使用了HashMap,这里在For循环中,会循环的进行反序列化,在ysoserial的payload中,实际上存储了2个对象,一个是恶意调用链TemplatesImpl,另一个是Proxy。所以这里的for循环会循环2次,每一次都put进HashMap中。

第一次反序列化是一个TemplatesImpl 然后put。 图中反序列化的e正是我们的TemplatesImpl,恶意代码存储在_bytecodes中。

然后进入put我们会发现,首先计算key的哈希 这个key就是我们上图中的e(TemplatesImpl),注意这个函数中还有个局部变量e,很容易混淆。然后根据hash来计算在table中的索引。

由于现在第一次table中还没有数据,则e为空,不会进入到for循环,而是直接调用addEntry去添加元素。 其中本次计算的hash 索引都在上图中,后面会用到。 然后put函数结束,回到第二层循环,这次反序列化的实际上是一个Proxy,这个Proxy用来代理TempLatesImpl,使用动态代理的时候会直接调用到Handler中的invoke函数。

回到第二层循环中 这里反序列化的结果实际上就是Proxy,payload中被强制转换成上图红线标注的类。 然后进入put,来计算proxy的hash。这个proxy的handler是AnnoationInvocationHandler

进入hash函数,发现是调用hashcode。

这里面的k就是上面传进来的Proxy,这里会触发动态代理机制,进入到handler中的invoke方法。 原来是调用handler中的hashCodeImpl方法。 在hashCodeImpl方法中,首先获得handler中memberValues的迭代器。这个memberValues就是我们构造的hashmap,key为“f5a5a608” 他的hashcode是0 value是templatesImpl恶意调用链,也就是我们第一次put进去的对象。 由于这个hashmap只有一个元素,所以迭代器这里只会循环一次,就出去了。所以0和X亦或,还是X。最终返回的是templatesImpl恶意调用链的hashcode。注意这里就是精华之处了。 hashCodeImpl函数结束完毕,会逐层返回到put中的hash函数 你会惊奇的发现 hash值和我们前面第一次put时候的hash一模一样。这就造成了hashset第一个元素的hash和第二个proxy的hash一样,于是他们获得的索引值i也就一样。 这里的i同样也是5。 那现在和第一次执行put的时候完全情况就不一样了,第一次我们是由于table返回的e是空的,没有进入到循环中,直接进行add添加元素去了。 这次i为5,正好e是TemplatesImpl对象。e不为空会进去到循环。 进入到循环之后,会调用key.equals函数。key的本质是proxy啊。会继续出发动态代理。 会去执行invoke,去调用equalsImpl方法。

equalsImpl方法会触发我们的恶意代码 首先获取所有的methods。然后循环的invoke。 第一次invoke的函数是var5。我们可以看到第一个就是newTransformer函数。剩下就是TemplatesImpl子调用链的过程了,详细去参考我之前的文章。 newTransformer函数就会导致bytecodes的加载。

这个调用链的思路和collections完全不一样,全程跪着调完。