
Python で画像を処理する PIL(Pillow)モジュールは、内部で GhostScript を呼び出すため、GhostButt 脆弱性(CVE-2017-8291)の影響を受け、リモートコマンド実行の脆弱性が発生します。
PIL は内部で画像ヘッダー(マジックバイト)に基づいて画像タイプを判別します。EPS ファイル(ヘッダーが %!PS)と判別された場合、PIL/EpsImagePlugin.py に処理が振り分けられます。
このモジュールでは、PIL はシステムの gs コマンド(GhostScript)を呼び出して画像ファイルを処理します:
command = ["gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dBATCH", # exit after processing
"-dNOPAUSE", # don't pause between pages,
"-dSAFER", # safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
# adjust for image origin
"-f", infile, # input file
]
# Omit the code to determine whether GhostScript is installed
try:
with open(os.devnull, 'w+b') as devnull:
subprocess.check_call(command, stdin=devnull, stdout=devnull)
im = Image.open(outfile)
-dSAFER(セーフモード)が設定されていますが、GhostScript(GhostButt CVE-2017-8291)のサンドボックスバイパス脆弱性により、このセーフモードがバイパスされ、任意のコマンドが実行可能となります。
さらに、現時点で最新の公式バージョンである GhostScript 9.21 でもこの脆弱性の影響を受けるため、オペレーティングシステムに GhostScript がインストールされている限り、PIL にはコマンド実行の脆弱性が存在すると言えます。
実行環境:
docker-compose up -d
起動後、http://your-ip:8000/ にアクセスするとアップロードページが表示されます。通常の機能は、PNG ファイルをアップロードすると、バックエンドが PIL を呼び出して画像を読み込み、幅と高さを出力するものです。しかし、実行可能なコマンドを含む EPS ファイルの拡張子を PNG に変更してアップロードすることができます。バックエンドはファイルヘッダーに基づいて画像タイプを判別するため、拡張子のチェックは無視されます。
例えば、poc.png をアップロードすると、touch /tmp/youhavebeenpwned を実行できます。POC 内のコマンドをリバウンドコマンドに変更すれば、シェルを取得できます。