Struts2で<s:url>を使う際のXSS混入
最近なにかと狙われがちの、Apache Struts 2。「.action」を通して任意のコードが実行できるという脆弱性はさんざんあちこちで書かれたので、ここでは、システム開発時のちょっとした注意点の小ネタをひとつ書いておこうと思います。
によるXSS
struts2の
以下、この事例を、実際に手を動かして確認できるように作ってみます。
Tomcatの準備
まずTomcatをインストールします。この手順を1からここで書くのはさすがに長すぎるので割愛します。以前にちこっと書いたのでそちらを参照してください(http://homepage1.nifty.com/y-osumi/works/code/tomcat7/)。
ここでは前段のApacheも省略して、Tomcatにはポート8080にブラウザで直接アクセスすることにします(Coyoteを利用します)。
一部のLinux環境では、iptablesの設定も必要でしょう。
/etc/sysconfig/iptables 内に、以下行を追加。 -A INPUT -m tcp -p tcp --dport 8080 -j ACCEPT
Struts 2のインストールとstruts2-showcase
Struts2では、showcaseというサンプルアプリケーション群が配布されています。こいつをひな形にして、XSS脆弱性を作り込んでみることにしましょう。
ダウンロードページに、Example Applicationsというパッケージがあります。これをダウンロードして展開すると、いくつかの.warファイルが出てきます。その中から、struts2-showcase.warをTomcatのwebappsに置いてデプロイします。
デプロイしたら、ブラウザからアクセスして確認します。ここではサーバのIPアドレスを10.211.55.18としていますので、URLは以下となります。
showscaseは、Struts2の様々な機能を紹介するサンプルアプリケーション群です。この中から、比較的XSS脆弱性を組み込みやすい(?)、Examples→CRUDを用いることにします。
jspと
まず、このアプリケーションのjspファイルを見てみましょう。struts2-showcase/empmanager/listEmployees.jspを見てみます。以下行に注目します。
<a href="<s:url action="edit-%{empId}" />"><s:property value="empId"/></a>
結論から言うと、struts2のshowcaseは、デフォルトでは攻撃はできません。例えば試しにこういうURLを入れてみます。
http://10.211.55.18:8080/struts2-showcase/employee/list.action?%22%20onMouseOver=%22alert%28document.cookie%29%22%20action=%22
このGET引数部分は、URLデコードすると以下のようになります。
" onMouseOver="alert(document.cookie)" action="
これはつまり、aタグのhrefにそのまま突っ込むと以下のようになることを狙ったわけです。
<a href="/struts2-showcase/employee/list.action?" onMouseOver="alert(document.cookie)" action="">
しかしこのCRUD Exampleでは、
namespaceをいじることによるXSS成立
では、他にURLにスクリプトを混入できる部分はないでしょうか。例えばパス名の部分に入れてみるのはどうでしょう。
http://10.211.55.18:8080/struts2-showcase/employee/%22%20onMouseOver=%22alert%28document.cookie%29%22%20action=%22/list.action
こうすると、存在しないURLなので当然のことながら404エラーが返ります。
しかし。Struts2のフレームワークでは、「存在しないパスを指定したから404が返る」ということは、実は当然ではないのです。
ここで、/struts2-showcase/WEB-INF/classes/struts.xmlの中を見てみると、以下のようにnamespaceという属性があります。
<package name="employee" extends="default" namespace="/employee"> <default-interceptor-ref name="crudStack"/> ... (省略) ... </package>
これは何でしょうか。では試しに、以下のようにnamespaceを空文字列にしてみましょう。
<package name="employee" extends="default" namespace="">
この反映にはTomcatの再起動が必要なので、Tomcatを再起動します。そして先ほど404が出ていたURLにアクセスしてみます。
http://10.211.55.18:8080/struts2-showcase/employee/%22%20onMouseOver=%22alert%28document.cookie%29%22%20action=%22/list.action
すると、なんたることでしょう。全然めちゃくちゃなパスを入れているのに、先ほどのCRUD Exampleの画面が表示されてしまいました。
この状態で、先ほどのa hrefにurlを突っ込んでいる部分をHTMLソースで確認してみると……以下のようになっています。見事にインジェクションできました。
<a href="/struts2-showcase/employee/" onMouseOver="alert(document.cookie)" action="/edit-1.action">1</a>
onMouseOverにJavaScriptのalert関数をインジェクションしていますので、これで"Alan"が登録してある「ID:1」のところにマウスカーソルを持っていくと……。
やりました、見事にXSSできています。
攻撃できてしまう仕組み
これは、Struts2のnamespaceの仕組みが問題の根底にあります。
Struts2では、Actionとそれを受けるJSP、という2つのセットを元に開発を進めて行きます。この際、namespaceによってURLとActionのマッピングを決めていくのですが、namespaceを空文字にすることもできます。この場合にはURLの最後のaction名から、Struts2フレームワークが受けのJSPをよろしく探してくれます(便利機能?)。そのためnamespace=""としているときは、URLに任意の文字列を混入することができ、結果として
もちろんきちんとnamespaceを設定していれば何も問題ないのですが、一部の入門書などに、まずnamespaceを空にして作ってみよう、みたいなものがあるみたいです(未確認)。また、namespaceを空にしているとパス名などがまだ未確定の際もゴリゴリとコーディングを進めてしまえるので、開発段階の最初期ではnamespaceを空にして作業するというフローもあるようです。
が、やはり危険すぎるため、「namespace="" は禁止」とするのが妥当です。(と思います)
というわけで小ネタすぎる気がしますが以上です。