TOUCH THE SECURITY Powered by Security Service G
こんにちは、脆弱性診断をしているhosoと申します。 先月、ISOG-JからWebシステム/Webアプリケーションセキュリティ要件書の更新版が出ましたね! 皆さんはもう目を通されましたか?
脆弱性名が直接書かれているわけではありませんが、実は4版には新たにSSTIと呼ばれる脆弱性への対処方法について追記されたものがあります。
脆弱性診断士として仕事をしている私ですが、Webアプリケーションセキュリティ要件定義書に取り上げられるまで、恥ずかしながらSSTIという脆弱性の存在を知りませんでした…。
そのような経緯からSSTIについて学んでいたのですが、これがとても面白い内容だったので簡単に紹介してみようと思ったのがこの記事の趣旨となります。
既にSSTIについて紹介されている記事いくつかあるため、すごく今更感もありますがお付き合いいただけますと幸いです。
SSTIとは
正式名称は「Server-Side Template Injection」となりこの脆弱性が内在している場合、テンプレート構文を利用して悪意のあるコードがサーバ内で実行される可能性があります。
SSTIが注目されている理由として、FlaskやDjangoといったテンプレートエンジンの機能を持つフレームワークを使ったWeb開発が盛んにおこなわれている背景があります。
Web開発には様々な言語やフレームワークが使われていますが、今回は【2022年】プログラミング言語おすすめランキング|言語の需要・将来性と年収目安で人気の言語であるpythonとそのフレームワークの1つであるFlaskを例に解説していきます。
仕事でWeb開発に関わっている方であれば、テンプレートエンジンについては知ってて当然の知識なのかもしれませんが、開発現場から数年以上離れている人や開発経験がない方の場合はなかなかイメージしにくい部分もあるかと思います。
そこで、SSTIの脆弱性の具体的な話に入る前に、まずテンプレートエンジンの仕組みについて見ていきましょう。
テンプレートエンジンとその仕組み
テンプレートエンジンとは、テンプレートというひな型に入力値(データ)を含んだ文字列を出力させる機能のことです。 テンプレートエンジンは、HTMlやメール生成処理などに広く使われています。
言葉で説明してもわかりにくいと思いますので、テンプレートエンジンを使った簡単なサンプルプログラム例に見ていきましょう。
今回、プログラムの構成例としてバックエンドとして動作するプログラム部分と一部の文字を動的に書きかえて表示するためのテンプレートの2つのソースコードを用意しました。
プログラムの内容としては、テキストボックスに入力した後に隣にあるsubmitボタンを押すと、その下に入力した内容が表示されるといったシンプルな仕組みとなっています。
実行するとどういった処理になるのかを見てみましょう。
from flask import Flask, render_template, request app = Flask(__name__) @app.route('/',methods=['GET', 'POST']) def base(): username = "" if request.method == 'POST': if request.form['username']: username = request.form['username'] return render_template('index.html', username = username) if __name__ == '__main__': app.run(debug=True)
※入力されたデータを受け渡して画面を描画しているpythonプログラム
<!DOCTYPE html> <html> <body> <form action="/" method="POST"> UserName : <input type="text" name="username" value=""> <input type="submit" value="submit"> </form> <h1> {{username}} </h1> </body> </html>
※画面を描画するテンプレート部分
実際にプログラムを実行してブラウザでアクセスしてみると、「userName:」と書かれた右側に入力欄とsubmitボタンの画面が表示されます。
試しにテキストフィールドに「PTCStest」と入れて「submit」を押してみます。
入力欄に入れた「ptcstest」という文字列が、HTMlの{{ username }}の部分を書き換えて表示されています。このように画面を動的に書き換えることが簡単にできるのがテンプレートエンジンの特徴です。
では次に、名前を表示している行の下に{{7*7}}と追記した状態でページを読み込んでみます。
<!DOCTYPE html> <html> <body> <form action="/" method="POST"> UserName : <input type="text" name="username" value=""> <input type="submit" value="submit"> </form> <h1> {{username}} </h1> <h1>{{7*7}}</h1> </body> </html>
{{7*7}}と表示されず、7*7の演算結果である「49」が表示されました。 これはテンプレートエンジンが1行上に書いてある入力文字を表示させる箇所と同様に、{{7*7}}を文字列として解釈せずコードとして解釈して実行したものであることがわかります。
今回はサーバ側で用意したテンプレート側を書き換えて確認したものになりますが、これが外部からの入力値で実行されてしまう場合、攻撃者により任意コードが実行されてしまう可能性があるため大変危険な状態となります。これがSSTIという脆弱性となります。
SSTI検証
では先ほどのサンプルプログラムを一部変更し、SSTIの脆弱性が内在するプログラムを見ていきます。
from flask import Flask, render_template_string, request app = Flask(__name__) @app.route('/',methods=['GET', 'POST']) def base(): username = "" if request.method == 'POST': if request.form['username']: username = request.form['username'] tmp = '\ <!DOCTYPE html>\ <html>\ <body>\ <form action="/" method="post">\ UserName:\ <input type="text" name="username" value="">\ <input type="submit" value="Submit">\ </form>\ <h1> %s </h1></body></html>' % username return render_template_string(tmp) if __name__ == '__main__': app.run(debug=True)
先ほどのサンプルコードとの違いは、テンプレートとなるHTMLをソース内に移し変数の値を直接テンプレートの中に入れて表示している点のみです。
この状態で入力欄に{{7*7}}を入れて実行すると、出力結果に49と表示されることからユーザからの入力値をそのままプログラム実行していることがわかります。
試しにrender_template_string関数で描画される前のtmpの値を見てみると、入力値を表示するタグの中に{{7*7}}が出力されています。つまり、{{}}が適切にエスケープされない状態でテンプレートにそのまま渡してしまったことで、SSTIの脆弱性が内在する状況を作り出してしまったことになります。
別のテンプレートエンジンの検証について
今回はFlask環境での解説をしてきましたが、テンプレートエンジンは数多く存在し、その構文もテンプレートエンジンによって様々です。
そうなると、どのテンプレートエンジンだとどのタグが有効なのかといった調査/検証をするのは大変ですよね。
実は今回のサンプルコードの参考にもさせて頂いているDiogoMRSilva/websitesVulnerableToSSTIというリポジトリを使うことで、様々なテンプレートエンジンの動作検証をすぐに行うことができます。
Dockerが使える環境を構築することが前提となりますので、使う場合はその点注意が必要です。検証可能なテンプレートエンジンと使用可能なタグについては、READMEファイルで表にまとめられています。
環境構築も簡単で、リポジトリをクローンした直下にあるstartAllDockersIndividually.shというファイルを実行するだけでdockerが立ち上がり、表に書かれたポート番号にアクセスすることで対象のテンプレートエンジンの検証に取り掛かることができます。
「色々なテンプレートエンジンについて検証してみたいけど、環境構築を用意するのは手間だな…」という方は、まずこちらのリポジトリを使ってみるのはいかがでしょうか。
まとめ
今回はSSTIの脆弱性の簡単に紹介してみました。
最近はWebアプリケーションの作りも複雑化してきています。 ユーザからの入力値については、安易に信用せずしっかりと検証する仕組みにするよう心掛けておくことをおすすめします。
また、冒頭でも紹介しておりますがISOG-Jが発表した「 Webシステム/Webアプリケーションセキュリティ要件書」を活用することで、今回紹介した脆弱性の他もチェックすることができます。 ぜひ活用してみてください!