Struts2で<s:url>を使う際のXSS混入

最近なにかと狙われがちの、Apache Struts 2。「.action」を通して任意のコードが実行できるという脆弱性はさんざんあちこちで書かれたので、ここでは、システム開発時のちょっとした注意点の小ネタをひとつ書いておこうと思います。

によるXSS

struts2タグは、不用意に使うとXSS(クロスサイトスクリプティング)を招き入れてしまう危険性を備えています。具体的には、struts.xmlのnamespaceの指定を""のように空文字にしている場合に、任意のJavaScriptを混入できる可能性があります。

以下、この事例を、実際に手を動かして確認できるように作ってみます。

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で使われるタグ機能です。現在のURLをそのまま突っ込むことができるので、このようにaタグのhref属性にリンク先を設定する際によく使われます。しかし、URLというのはユーザが任意に入力できる値であり、すなわち攻撃者も任意のコードを入れ込むことができる部分です。こんな設定でだいじょうぶなんでしょうか。

結論から言うと、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では、のGET引数は切り捨てられるため攻撃は成立しません。

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="" は禁止」とするのが妥当です。(と思います)

というわけで小ネタすぎる気がしますが以上です。