
fastjson 원격 클래스 로드 RCE를 위한 Docker 랩 + 방어 스캐너. fastjson 1.2.66-1.2.83: @JSONType 리소스 프로브(CVE-2026-16723). fastjson2 2.0.57: 공격자 @type이 다형성 유형(@JSONType(seeAlso) / Jackson @JsonSubTypes)을 통해 autoType이 비활성화된 loadClass에 도달합니다. 마커 전용 페이로드; safeMode + JDK17 제어.
@JSONType 원격 JAR/FD 체인 연구소 및 탐지이 저장소는 이제 두 개의 별도 Fastjson 연구 트랙을 제공합니다:
modern-fd/는 권장되는 마커 전용 재구성으로, Spring Boot 3, 일반 임베디드 Tomcat 및 JDK 17에서 Fastjson 1.2.83 단일 본문 원격 JAR/파일 디스크립터 체인을 재현합니다. AutoType을 비활성화한 상태에서 리터럴 고정 DTO 싱크인 JSON.parseObject(body, BoundEnvelope.class)를 사용합니다.scanner/에는 수동 요청/로그 탐지, 정적 아티팩트 인벤토리 및 실행되지 않는 도달 가능성 프로브가 포함되어 있습니다.버그:
ParserConfig.checkAutoType는getResourceAsStream(typeName.replace('.','/') + ".class")를 수행하여 모든@type값에 대해@JSONType어노테이션을 검사합니다. 최신 Linux/JDK 조합에서는 원격jar:http프로브가 열린 디스크립터 뒤에 JAR을 캐시할 수 있습니다. 동일한 본문에서 나중에jar:file:/proc/self/fd/N프로브는 유효한 클래스 이름으로 다시 열고 어노테이션이 달린 클래스를 초기화할 수 있습니다. 정확한 1.2.83 구성은 AutoType 비활성화 상태에서List<Object>를 포함하는 고정 DTO를 통해 작동합니다. 따라서 바인딩만으로는 완화책이 아닙니다. 전체 설명:docs/MECHANISM.md.
동반된 전체 프로젝트 검토는 모든 193개의 Fastjson 1.2.83 프로덕션 Java 파일에 걸쳐 2,041개의 호출 가능/정적 초기화 행을 다루었습니다. 아래 표는 가장 영향력이 큰 결과를 우선순위로 매깁니다. 모든 Fastjson 배포가 영향을 받는다는 주장은 아닙니다. 이 저장소는 F105를 직접 재현합니다. 나머지 항목은 별도로 봉인된 소스 검토와 제한된 연구소 증거를 요약합니다.
| 우선순위 | 발견 | 입증된 결과 | 중요한 경계 |
|---|---|---|---|
| 1 | F105 — 원격 @JSONType 바이트코드 실행 | JDK 8 직접 경로 및 Linux/JDK 17 유지 JAR /proc/self/fd/N 연속성을 포함한 호환 가능한 Spring Boot 로더를 통한 공격자 제공 클래스 초기화 | 파서가 도달 가능한 @type 캐리어, 호환 가능한 Boot/TCCL 로더, 이그레스, SafeMode/IgnoreAutoType 비활성화, 정확한 공격자 JAR 필요. 최신 고정 DTO 경로는 추가로 일반 값 레인(연구소는 List<Object> 사용), Linux procfs, 유지 디스크립터 및 정확한 이름 정렬 필요; 모든 JDK, 로더 또는 OS에서 보편적이지 않음 |
| 2 | F1/F45 — TemplatesImpl 명령 실행 | 무시된 본문 속성을 포함한 고정 DTO API를 통한 마커 명령 실행 | 약화된 서버 정책 필요: 클래스 허용, AutoType, 비공개 필드 채우기 또는 동등한 경로; 기본 설정에서는 차단됨 |
| 3 | F70/C016 — 무제한 버퍼링 및 GZIP 확장 | 타입이 지정된 byte/InputStream 및 어노테이션이 달린 DTO 경로가 출력 제한 없이 확장 또는 버퍼링됨; 제한된 힙 Java OOME 재현됨 | 엔드포인트/스키마 및 공격자 바이트 도달 가능성 필요; 프로덕션 전반 중단은 입증되지 않음 |
| 4 | F18/C089 — 파서 스레드 스택 고갈 | 깊이 중첩된 일반 객체 값이 기본 파싱에서 StackOverflowError 발생 가능 | 요청 스레드 가용성 영향; 불변의 JVM 또는 서비스 전체 충돌 아님 |
| 5 | F120/C162 — 고정 스키마 HTTP SSRF/로컬 리소스 로딩 | 본문 제어 JEditorPane/JTextPane.page가 HTTP 요청을 수행하고 제한된 file: 콘텐츠를 Document 상태로 로드 가능 | 애플리케이션이 Swing 타입을 선언해야 함; 자동 응답 유출 또는 RCE는 입증되지 않음 |
| 6 | F118/C160 — 해시 충돌 인증/데이터 무결성 실패 | 고유한 유니코드 FNV-1a-64 충돌이 특권 enum 상수에 바인딩되거나 JSONPath 변형을 잘못된 빈 세터로 라우팅 가능 | 사용 가능한 충돌 및 다운스트림 신뢰 또는 노출된 JSONPath 변형 작업 필요; 클래스 허용 또는 RCE 없음 |
| 7 | F121/C163 — 고정 JdbcRowSet JNDI 도달 가능성 | 정확한 세터 순서로 인해 본문 선택된 아웃바운드 JNDI/LDAP 연결 하나 발생 | 증거는 연결에서 중단됨: 네이밍 응답, 객체 팩토리, 바이트코드 로딩 또는 RCE 없음 |
| 8 | F2/F45 — 기본 DNS 해석 | 공격자 제어 호스트명 해석이 기본 구성에서 작동하며 무시된 고정 DTO 속성을 통해 작동 | DNS/OOB 상호작용만 가능, 일반 HTTP SSRF 또는 RCE 아님 |
검증 레이블, 전제 조건, 하위 계층 발견, 수정 우선순위 및 명시적 부정 경계는 findings/FINDINGS.md를 참조하십시오.
이 저장소는 귀하가 소유하거나 명시적으로 테스트를 허가받은 시스템의 교육, 방어 연구 및 승인된 테스트를 위한 것입니다. 최신 연구소의 내부 네트워크 지원 시 호스트 게시는 127.0.0.1에서만 요청됩니다. 해당 스크립트는 완전히 격리된 네트워크 내에서 테스트를 실행합니다. 최신 연구소는 프로세스 실행 프리미티브가 없으며 고정 JVM 내 마커만 설정합니다. 레거시 JDK 8 연구소는 id를 실행하고 출력을 /tmp/PWNED에 기록합니다. 격리된 상태를 유지하십시오. 레거시 익스플로잇이나 활성 프로브를 승인되지 않은 시스템에 지시하지 마십시오.
scanner/ 디렉토리에는 네 가지 의존성 없는 Python 3 도구(pip 설치 필요 없음)가 포함되어 있습니다.
fjdetect.py — 수동으로 디코딩된 JSON 요청 본문 또는 구조화된 JSON 로그에서 원격 JAR 시드와 /proc/self/fd 또는 /dev/fd 시퀀스를 검사합니다.fjscan_static.py — 취약한 조합(콘텐츠 기반, 메타데이터 확인된 1.2.83 × 실제 Spring Boot 로더 클래스 콘텐츠)에 대해 jars/wars/ears를 인벤토리합니다. 프로브가 포함된 1.2.48–1.2.82 릴리스 및 메타데이터/파일명 전용 후보는 검토를 위해 별도로 보고됩니다. CI 게이트에 친화적(EXPOSED 시 exit 2).fjscan_probe.py — 능동적이고 안전한 도달 가능성 확인: 사용자가 제어하는 카나리(내장 리스너 또는 Burp Collaborator / interactsh)에 @type을 발사하고 리소스 가져오기 콜백을 상호 연관시킵니다. 내장 리스너는 빈 404를 반환합니다. 외부 리스너도 동일하게 구성하십시오. 콜백이 클래스 로딩이나 RCE를 증명하지는 않습니다.fjpayload.py — 수동 테스트를 위한 원격 리소스 가져오기 본문을 생성합니다. 실행이 의도되지 않은 경우 빈/404 리스너만 사용하십시오.# 1) passive request/log inspection
python3 scanner/fjdetect.py --ndjson gateway.jsonl
# 2) inventory build artifacts / unpacked images (parallel)
python3 scanner/fjscan_static.py /path/to/artifacts --threads 16
# 3) active probe across many domains — simplest: --auto fires baseline + plain + escaped comparison
# DNS probes per target and prints a rollup; DNS callbacks land in your Burp Collaborator
python3 scanner/fjscan_probe.py --collaborator <sub>.oastify.com --auto --targets domains.txt --threads 50
cat domains.txt | python3 scanner/fjscan_probe.py --collaborator <sub>.oastify.com --auto --targets -
# 4) bulk fetch payloads for manual testing (one per domain, stable correlation token)
python3 scanner/fjpayload.py <collab-or-ip> --targets-file domains.txt
domains.txt = 줄당 하나의 대상 — 전체 [METHOD ]URL 또는 베어 도메인(--scheme / --target-port / --path로 확장됨); # 주석 허용:
api.internal.example
POST https://svc.example/v1/ingest
10.0.0.7:8080
fjscan_probe.py (능동 프로브)
| 플래그 | 기본값 | 용도 |
|---|---|---|
--auto | off | 가장 간단함 — 대상별로 baseline + plain + Unicode-이스케이프 비교 DNS 프로브를 보내고 요약 출력; --collaborator + --targets만 필요 |
--canary-ip <ip> | — | 내장 리스너 모드; 대상이 보는 이 호스트의 IP |
--collaborator <v> | — | 외부 OOB (IPv4 / int / 점 없는 호스트 / Collaborator 서브도메인) |
--probe-type <t> | jsontype | 수동 모드: jsontype = 원격 리소스 가져오기 경로 (jar:http int-IP); dns = 점이 있는 호스트를 통한 Inet4Address OOB (Collaborator 호환); both |
--targets <file|-> | — | 비어 있지 않은 대상 목록; -는 stdin 읽음 (필수); 빈/주석 전용 입력 시 0이 아닌 종료 |
--threads N | 20 | 동시 요청 워커 |
--header 'K: V' | — | 추가 요청 헤더 (반복 가능); 대표적인 프론트엔드 요청을 위해 브라우저 유사 --user-agent가 기본적으로 전송됨 |
--baseline '{"a":1}' | — | 또한 대상별로 양성 제어 본문 전송; baseline/프로브 응답 차이는 콘텐츠에 민감한 에지, 미들웨어 또는 애플리케이션 처리와 일관되며 확인이 필요함 |
--max-redirects N | 3 | 최대 N개의 동일 출처 리디렉션 또는 표준 동일 호스트 HTTP:80→HTTPS:443 업그레이드 따름 (0 = 없음); HTTPS 다운그레이드, 임의 포트/출처 변경, 교차 호스트 리디렉션은 차단됨; 업그레이드는 기본 비밀 헤더만 유지 |
--evasion {ukey,uval,both} | none | \uXXXX-이스케이프 @type 키 / 클래스 값 (fastjson은 여전히 디코딩함); 회피 전용 콜백은 디코딩된 프로브 데이터가 리터럴 키워드 필터를 통과하는 것과 일치하지만 여전히 파서/버전 확인 필요 |
--scheme / --target-port / --path | http / — / /parse | 베어 도메인이 확장되는 방식 |
--method | POST | 기본 HTTP 메서드 |
--wrap '{"u":{{P}}}' | {{P}} | 프로브 객체를 필드 안에 중첩 |
--listen-port / --port | 19000 / 80 | 내장 카나리 포트 / collaborator 포트 |
--wait / --timeout | 10 / 8 | 콜백 대기 시간(초) / 요청당 타임아웃(초) |
fjscan_static.py (인벤토리): <paths…> · --threads N (기본 8) · --json (기계 출력; EXPOSED가 하나라도 있으면 종료 코드 2).
fjpayload.py (생성기): <collaborator> · --targets-file/-f <file\|-> (대량) · --port · --token · --wrap · --entry.
Collaborator 참고:
@JSONTypejar:싱크는typeName.replace('.','/')를 수행하므로 호스트의 모든 점이 슬래시가 됩니다 — 점이 있는 Burp Collaborator 서브도메인(abc.oastify.com)은 이를 통해 전달될 수 없습니다(도구는 정수 IP + URL 경로 토큰으로 대체). 공개 Collaborator로 콜백을 받으려면 **--probe-type dns**를 사용하십시오:java.net.Inet4Address프리미티브는 점이 있는 호스트를 허용하고 DNS 상호작용(<tok>.<sub>.oastify.com)을 발생시켜Inet4Address파싱 프리미티브 + 이그레스를 확인합니다. 이는 전제 조건 증거이며 원격 JAR/FD 터미널의 증거는 아닙니다.scanner/README.md를 참조하십시오.
요구 사항: Docker + Docker Compose, 그리고 첫 빌드 시 Maven Central에 대한 아웃바운드 액세스 (fastjson / spring-boot-loader / asm을 가져오기 위해).
cd modern-fd
./scripts/static-safety-check.sh
./scripts/run-positive.sh
./scripts/run-controls.sh