专栏名称: 合天网安实验室
为广大信息安全爱好者提供有价值的文章推送服务!
目录
相关文章推荐
高校人才网V  ·  潍坊科技学院2025年公开招聘人员公告 ·  3 天前  
高校人才网V  ·  上海健康医学院2025年公开招聘工作人员公告 ·  3 天前  
半导体行业联盟  ·  惨烈!ST CEO 被提议罢免! ·  2 天前  
半导体行业联盟  ·  宇树科技,落子上海! ·  3 天前  
半导体行业联盟  ·  华为重回前十!DeepSeek未上榜! ·  3 天前  
51好读  ›  专栏  ›  合天网安实验室

浅谈黑盒识别Fastjson/Jackson组件

合天网安实验室  · 公众号  ·  · 2024-06-28 16:30

正文

Java生态中基本只有Jackson和Fastjson组件,但是两者相关的版本均存在相应的漏洞(反序列化、DDOS),所以对目标是否使用了对应的组件需要有相关的判断方法。方便信息收集进行进一步的测试。

Java生态中基本只有Jackson和Fastjson组件,但是两者相关的版本均存在相应的漏洞(反序列化、DDOS),那么如何有效识别目标使用了哪种对应的组件就很有必要了。

理想状态下 如果站点有原始报错回显,可以用不闭合花括号的方式进行报错回显 ,报错中往往中会有Fastjson/Jackson的关键字:

Jackson:

Fastjson:

但是实际上并不可能那么的理想,所以需要一些其他的trick来进行区分。下面探讨下两个解析器之间有什么区别。

0x01 Fastjson&Jackson中的Feature

FastJson和Jackson在序列化和反序列化的过程中提供了很多特性(Feature),例如Fastjson的Feature.DisableFieldSmartMatch(1.2.30引入)。如果没有选择该Feature,那么在反序列的过程中,FastJson会自动把下划线命名的Json字符串转化到驼峰式命名的Java对象字段中。

简单看下两个解析器是如何加载Feature的。

1.1 Fastjson

1.2.24版本 为例,查看常用的解析方法,在对json文本进行解析时,一般会使用JSON.parse(text),默认配置如下:

public static Object parse(String text) {
return parse(text, DEFAULT_PARSER_FEATURE);
}

DEFAULT_PARSER_FEATURE是一个缺省默认的feature配置:

public static int DEFAULT_PARSER_FEATURE;
static {
int features = 0;
features |= Feature.AutoCloseSource.getMask();
features |= Feature.InternFieldNames.getMask();
features |= Feature.UseBigDecimal.getMask();
features |= Feature.AllowUnQuotedFieldNames.getMask();
features |= Feature.AllowSingleQuotes.getMask();
features |= Feature.AllowArbitraryCommas.getMask();
features |= Feature.SortFeidFastMatch.getMask();
features |= Feature.IgnoreNotMatch.getMask();
DEFAULT_PARSER_FEATURE = features;
}

可以通过Feature类的isEnabled方法来判断相关的Feature是否开启:

package com.alibaba.fastjson.parser;

public enum Feature
{
AutoCloseSource, AllowComment, AllowUnQuotedFieldNames, AllowSingleQuotes, InternFieldNames, AllowISO8601DateFormat, AllowArbitraryCommas, UseBigDecimal, IgnoreNotMatch, SortFeidFastMatch, DisableASM, DisableCircularReferenceDetect, InitStringFieldAsEmpty, SupportArrayToBean, OrderedField, DisableSpecialKeyDetect, UseObjectArray, SupportNonPublicField;

public final int mask;

private Feature()
{
this.mask = (1 << ordinal());
}

public final int getMask()
{
return this.mask;
}

public static boolean isEnabled(int features, Feature feature)
{
return (features & feature.mask) != 0;
}
......
}

1.2 Jackson

Jackson主要是在com.fasterxml.jackson.core.JsonFactory对Feature进行管理。在类加载时会先把相关Feature的默认值进行采集:

每个Feature都会有自己的默认值,例如下图中的USE_BIG_DECIMAL_FOR_FLOATS主要是将浮点数反序列化为BIG_DECIMAL,默认是False:

同样的,springboot在org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration进行装配时,如果没有其他配置,会把这些默认的Feature配置进行装载:

既然两者都在在序列化和反序列化的过程中提供了很多特性(Feature),而两者之间的Feature肯定是有区别的,可以利用这一点看看能不能找到一些思路用户两者的区分。

0x02 黑盒区分Fastjson和Jackson

2.1 通过默认Feature配置区分

根据前面的思路,可以根据两者默认的Feature配置或者设计上的区别来进行区分。下面列举一些可用的trick。

2.1.1 Jackson的JsonParser.Feature(2.10后替换为JsonReadFeature)

JsonReadFeature的配置也是一样的:

public enum JsonReadFeature implements FormatFeature {
ALLOW_JAVA_COMMENTS(false, JsonParser.Feature.ALLOW_COMMENTS),
ALLOW_YAML_COMMENTS(false, JsonParser.Feature.ALLOW_YAML_COMMENTS),
ALLOW_SINGLE_QUOTES(false, JsonParser.Feature.ALLOW_SINGLE_QUOTES),
ALLOW_UNQUOTED_FIELD_NAMES(false, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES),
ALLOW_UNESCAPED_CONTROL_CHARS(false, JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS),
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false, JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER),
ALLOW_LEADING_ZEROS_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS),
ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS),
ALLOW_NON_NUMERIC_NUMBERS(false, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS),
ALLOW_MISSING_VALUES(false, JsonParser.Feature.ALLOW_MISSING_VALUES),
ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA);
......
}

这里以JsonParser.Feature为例进行举例:

  • 解析value遇到以"0"为开头的数字

Jackson的objectMapper默认情况下是不能解析以"0"为开头的数字的,但是fastjson是可以的:

/**
Feature that determines whether parser will allow JSON integral numbers to start with additional (ignorable) zeroes (like: 000001). If enabled, no exception is thrown, and extra nulls are silently ignored (and not included in textual representation exposed via getText).
Since JSON specification does not allow leading zeroes, this is a non-standard feature, and as such disabled by default.
**/

ALLOW_NUMERIC_LEADING_ZEROS(false),

Fastjson会把01解析成1:

Jackson在解析01时会抛出异常:

  • 解析value为NaN
    Jackson的ObjectMapper解析器默认不能识别 "Not-a-Number" (NaN),不会认为其为浮点类型或者int类型的数字:

/**
Feature that allows parser to recognize set of "Not-a-Number" (NaN) tokens as legal floating number values (similar to how many other data formats and programming language source code allows it). Specific subset contains values that XML Schema (see section 3.2.4.1, Lexical Representation) allows (tokens are quoted contents, not including quotes):
"INF" (for positive infinity), as well as alias of "Infinity"
"-INF" (for negative infinity), alias "-Infinity"
"NaN" (for other not-a-numbers, like result of division by zero)
Since JSON specification does not allow use of such values, this is a non-standard feature, and as such disabled by default.
**/

ALLOW_NON_NUMERIC_NUMBERS(false)

Fastjson 1.2.70会把NaN解析成0:

Fastjson 1.2.37会抛出异常:

Jackson会抛出异常:

  • 注释符

当json字符串里存在注释符时,默认情况下Jackson的ObjectMapper解析器不能解析(Fastjson的AllowComment默认是开启的,所以支持注释符的解析):

/**
* Feature that determines whether parser will allow use
* of Java/C++ style comments (both '/'+'*' and
* '//' varieties) within parsed content or not.
*


* Since JSON specification does not mention comments as legal
* construct,
* this is a non-standard feature; however, in the wild
* this is extensively used. As such, feature is
* disabled by default for parsers and must be
* explicitly enabled.
*/


ALLOW_COMMENTS(false)

Fastjson支持注释符:

Jackson默认情况下会报错:

  • json字段使用单引号包裹

Fastjson的Feature.AllowSingleQuote 是默认开启的,支持使用单引号包裹字段名,但是jackson受到JsonParser.Feature.ALLOW_SINGLE_QUOTES的影响,默认是不支持的:

/**
Feature that determines whether parser will allow use of single quotes (apostrophe, character '\'') for quoting Strings (names and String values). If so, this is in addition to other acceptable markers. but not by JSON specification).
Since JSON specification requires use of double quotes for field names, this is a non-standard feature, and as such disabled by default.
**/

ALLOW_SINGLE_QUOTES(false)

Fastjson正常解析:

Jackson解析抛出异常:

  • json属性没有使用双引号包裹

fastjson的AllowUnQuotedFieldNames默认开启,允许json字段名不被引号包裹,但是jackson的ALLOW_UNQUOTED_FIELD_NAMES默认不开启,无法解析:

/**
* Feature that determines whether parser will allow use
* of unquoted field names (which is allowed by Javascript,
* but not by JSON specification).
*


* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by default.
*/


ALLOW_UNQUOTED_FIELD_NAMES(false)
  • 解析JSON数组中“缺失”的值

如果数组中两个逗号之间缺失了值,形如这样 [value1, , value3] 。对于fastjson来说可以解析,jackson受到 ALLOW_MISSING_VALUES 的影响会抛出异常:

/**
Feature allows the support for "missing" values in a JSON array: missing value meaning sequence of two commas, without value in-between but only optional white space. Enabling this feature will expose "missing" values as JsonToken.VALUE_NULL tokens, which typically become Java nulls in arrays and java.util.Collection in data-binding.
For example, enabling this feature will represent a JSON array ["value1",,"value3",] as ["value1", null, "value3", null]
Since the JSON specification does not allow missing values this is a non-compliant JSON feature and is disabled by default.
**/

ALLOW_MISSING_VALUES(false)

Fastjson正常解析,会把缺失的值忽略掉:

Jackson会抛出异常:

2.1.2 Jackson的MapperFeature

  • 大小写敏感

假设Bean的结构如下:

public class User {
private int id;
private String userName;
private String sex;
private String[] nickNames;

//对应的getter和setter方法
}

在代码里里属性id是小写的,在fastjson和jackson解析时会有区别。

FastJson在反序列化的时候,是对大小写不敏感的:

在Jackson中, MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES 默认设置为FALSE,在反序列化时是大小写敏感的,可以看到下面的例子中Id因为大小写敏感的问题并未赋值:

2.1.3 Fastjson的Feature

  • 忽略json中包含的连续的多个逗号

Fastjson中Feature.AllowArbitraryCommas是默认开启的,允许在json字符串中写入多个连续的逗号。

Fastjson正常解析:

Jackson会抛出异常,类似的的Feature是ALLOW_TRAILING_COMMA(是否允许json尾部有逗号,默认是False):

2.2 结合fastjson智能匹配区分

除了通过默认Feature的差异以外,FastJSON存在智能匹配的特性,即使JavaBean中的字段和JSON中的key并不完全匹配,在一定程度上还是可以正常解析的。通过这些特性也可以简单的进行区分。

  • 字段名包含 - _

主要是在JavaBeanDeserializer.smartMatch方法进行实现。通过这一特点可以在一定程度上做区分。

1.2.36版本及后续版本 ,部分具体代码如下,具体处理方法在TypeUtils.fnv1a_64_lower:

public FieldDeserializer smartMatch(String key, int[] setFlags)
{
if (key == null) {
return null;
}
FieldDeserializer fieldDeserializer = getFieldDeserializer(key, setFlags);
if (fieldDeserializer == null)
{
long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
if (this.smartMatchHashArray == null)
{
long[] hashArray = new long[this.sortedFieldDeserializers.length];
for (int i = 0; i < this.sortedFieldDeserializers.length; i++) {
hashArray[i] = TypeUtils.fnv1a_64_lower(this.sortedFieldDeserializers[i].fieldInfo.name);
}
Arrays.sort(hashArray);
this.smartMatchHashArray = hashArray;
}

查看TypeUtils.fnv1a_64_lower的具体实现,这里忽略字母大小写和-和_:

 public static long fnv1a_64_lower(String key)
{
long hashCode = -3750763034362895579L;
for (int i = 0; i < key.length(); i++)
{
char ch = key.charAt(i);
if ((ch != '_') && (ch != '-'))
{
if ((ch >= 'A') && (ch <= 'Z')) {
ch = (char)(ch + ' ');
}
hashCode ^= ch;
hashCode *= 1099511628211L;
}
}
return hashCode;
}

也就是说fastjson1.2.36版本及后续版本支持同时使用_和-对字段名进行处理:

但是jackson默认是没有这一特性的,例如下面的例子,并没有识别到经过 -







请到「今天看啥」查看全文