
CVE ID: CVE-2023-36664
製品: Ghostscript (< 10.01.2)
脆弱性タイプ: リモートコード実行 (RCE)
本脆弱性(CVE-2023-36664)は、Ghostscriptがパイプデバイス(%pipe%または|プレフィックス)に対するパス権限検証を不十分に処理することによって発生する任意コード実行(RCE)脆弱性である。
攻撃者によって悪意を持って操作された文書ファイル(PS/EPS)をシステムがパースまたは処理する過程で、ユーザーの許可なしに任意のシステムコマンドが実行される可能性がある。
主な役割: 本質的にテキストで構成されたグラフィック座標コード(例:「100 200の位置に線を引け」)を読み取り、私たちが目で見ることができるモニター画面やプリンター出力用の画像ファイルに変換する役割を担う。
パイプは、あるアプリケーションの出力を別のアプリケーションの入力に接続し、ソフトウェア間で相互に通信できるようにする機能である。コマンドでは|記号で表される。
例: cat /etc/hosts | grep localhost
Ghostscriptは、基本的にセーフモード(-dSAFER)が有効になっている場合、システムの機密ファイルにアクセスしたり外部コマンドを勝手に実行したりできないように、ファイルパスを厳格に検査する。
問題点: 攻撃者がファイルパスの位置に通常のファイル名ではなくパイプデバイスのプレフィックス(%pipe%または|)を精密に操作して押し込んだ場合、Ghostscript内部の権限検証ロジックがこのプレフィックスを正しく認識できず、「安全なパス」または「検証対象外のもの」と誤認して通過させてしまう。
結果: 本来であれば弾かれるべき危険なコマンドが、検証ループをあっさりと迂回してしまうことになる。
悪意のあるファイル注入: 攻撃者がPostScript(.ps/.eps)ファイル内部に(%pipe%悪意あるコマンド) (モード) file /DCTDecode filterのような形式でコードを埋め込む。
パースおよび誤認: Ghostscriptがこのファイルを読み取って処理する過程で、権限検証段階(門番)をエラーなく通過する。
OSシェルへの受け渡し: 検証を通過したコマンドストリームが、パイプデバイス機能によってLinuxのshやWindowsのcmdなどのOS内部シェルにそのまま渡され、バックエンド権限で実行される。
パッチされたコードを見ると、次の2つの.cファイルが修正されたことが確認できる。
base/gpmisc.cbase/gslibctx.cここではgpmisc.cを見ながら脆弱性を理解すればよい。

この脆弱性の核心は、%pipe%のような特殊パスを通常のファイルパスと正しく区別できず、そのまま通常のパスとして認識してしまうことにある。
gp_file_name_reduceこれを確認するために、ファイルパスを整理する関数を見てみる必要がある。gpmisc.cでパスを整える役割を担う関数はgp_file_name_reduce(...)であり、この関数は内部的にgp_file_name_combine()を呼び出してその結果を返す。
gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen) {
return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
}
gp_file_name_combine()は、その名前の通り、受け取ったファイルパスから./や//のような不要な相対パス表現を除去する関数である。
問題はここで発生する。もしこの関数に通常のファイルパスではなく、%pipe%のようにコマンド実行を誘導する特殊な文字列が入力されると、関数はこれを正常なパスパターンとして認識できず、何の処理もせずに元の文字列をそのまま返す。
gp_validate_path_len
gp_validate_path_len(...)は、内部的にgp_file_name_reduce()を呼び出してパスを検証する関数だが、この過程で入力値が通常のファイルパスなのか%pipe%のようなコマンド実行構文なのかを区別する独立した検証ロジックは存在しない。
結果的に、%pipe%を含む文字列が何のフィルタリングもなくそのまま通過することになり、これが今回の脆弱性の根本原因である。
したがって、%pipe%touch /tmp/pwnedのような文字列がファイルパスとして渡されると、ユーザーは単に画像ファイルをレンダリングしようとしただけなのに、実際にはtouch /tmp/pwnedコマンドが実行され、/tmp/pwnedファイルが生成される結果につながる。
┌─────────────────────────────────┐
│ Docker Container │
│ (Ubuntu 22.04 + Ghostscript) │
│ │
│ /home/test/ ← Working directory
│ ├── poc.py ← PoC 생성 스크립트
│ | |
│ └── /var/www/html/config.php ← 민감 정보 파일
│ │
│ User: test (비 root) │
│ Ghostscript: 10.01.1 (취약) │
└─────────────────────────────────┘
ここでrootアカウントではなく一般アカウントを選択した理由は、実際の運用サーバーではセキュリティ上、rootアカウントを直接使用しないのが一般的だからである。したがって、実際のサーバーが攻撃される状況を可能な限り忠実に再現するために、一般アカウント基準で環境を構成した。
version: '3.8'
services:
gs-lab:
build: .
container_name: cve_lab
network_mode: "host"
environment:
- DISPLAY=${DISPLAY}
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix:ro
- ./poc.py:/home/test/poc.py
stdin_open: true
tty: true
DISPLAY=${DISPLAY}: ホストのX11ディスプレイサーバーにコンテナ内のGUIアプリケーションがアクセスできるように環境変数を渡す。/tmp/.X11-unix:/tmp/.X11-unix:ro: ホストのX11ディスプレイサーバーとコンテナ間の通信用Unixソケットをマウントする。
./poc.py:/home/test/poc.py: ホストのローカルPoCスクリプトをコンテナの実行環境にマウントする。
FROM ubuntu:22.04
RUN apt update && \
apt install -y \
wget \
build-essential \
gedit \
python3 \
sudo && \
rm -rf /var/lib/apt/lists/*
RUN wget https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10011/ghostscript-10.01.1.tar.gz && \
tar -xzf ghostscript-10.01.1.tar.gz
WORKDIR /ghostscript-10.01.1
RUN ./configure && \
make && \
make install
RUN mkdir -p /var/www/html && \
echo "DB_PASSWORD=SuperSecret1234!!!" > /var/www/html/config.php
RUN useradd -m -s /bin/bash test
WORKDIR /home/test
USER test
CMD ["/bin/bash"]
基本的なPoC環境を構成するには、Ghostscriptの脆弱バージョンを取得するためのwget、tar.gzの圧縮を解いた後にソースをインストールするためのbuild-essential、.ps実行時に悪意を持って起動させるメモ帳(gedit)などがある。
Ghostscriptの脆弱バージョン10.01.1は、gitからソースを取得してインストールした。RUN ./configure && \ make && \ make installは、ソースをダウンロードしたフォルダに使用方法が記載されていたので、それに従ってインストールしたものである。
通常、/var/www/html/config.phpにはWebアプリケーションの核心的な設定値が入る。例えばデータベース名、パスワード そこで、任意にDBパスワードを奪取する想定でconfig.phpファイルを生成する。
rootアカウントではなく一般アカウントtestでPoCを行う予定なので、新しいユーザーを作成する。
# Docker 이미지 빌드
docker compose -f docker-compose.yml up -d
# 컨테이너 실행
docker exec -it cve_lab bash
コンテナ内で:

python3 poc.py -p "gedit /var/www/html/config.php" -m r -f test
生成結果:

入力した悪意のあるパスが.ps内に正しく入っていることが確認できる。
gs -dNOSAFER test.ps

geditが自動的に開き、/var/www/html/config.phpの内容が露出する。
DB_PASSWORD=SuperSecret1234!!!

公式に脆弱性が修正されたGhostscript 10.01.2以上の最新バージョンへアップデートを実行する必要がある。
公式パッチでは、gp_validate_path_len()関数内でgp_file_name_reduce()を呼び出す前に検証を行うロジックが追加された。
パイプ特殊文字列の例外処理およびブロックの強制: 入力パスのプレフィックスが%pipe%または|記号で始まる場合を、別途完全に検出する分岐文が追加された。
公式パッチバージョン: https://github.com/ArtifexSoftware/ghostpdl/commit/5f56c6f6f989816fc9cc671116740acecbed5b6c