最近、セキュリティFIXのリリースが多いですね。良いことであると捉えるべきか…
EOLになったStruts1.xからの乗り換えにより、『目玉の数が増えた』というところでしょうか。
さて、今回のFIX対象も例によってOGNL絡みです。
http://struts.apache.org/release/2.3.x/docs/s2-015.html
S2-015
Struts 2 allows define action mapping base on wildcards, like in example below:
/example/{1}.jsp
If a request doesn't match any other defined action, it will be matched by * and requested action name will be used to load JSP file base on the name of action.
And as value of {1} is threaten as an OGNL expression, thus allow to execute arbitrary Java code on server side.
This vulnerability is combination of two problems:
- requested action name isn't escaped or checked agains whitelist
- double evaluation of an OGNL expression in TextParseUtil.translateVariables when combination of $ and % open chars is used.
Struts2では、ワイルドカードを使ったアクションマッピングが出来る。
例えば以下の用に定義した時、
/example/{1}.jsp
もしリクエストが他に定義されたいずれのアクションにもマッチしないならば、このアクション定義はワイルドカード*によりマッチし、
リクエストされたアクション名はJSPファイルとのマッチングで用いられる。(訳注: result定義の"/example/{1}.jsp"のこと)この時、result内の{1}に当てはまるパラメータ(すなわちアクション名)がOGNL式だった時、任意のJavaコードを実行できてしまう。
問題の根本原因は次の通り。
- リクエストされたアクション名はエスケープもされず、ホワイトリスト方式のチェックもない
- "${"または"%{"から始まるOGNL式がTextParseUtil#translateVariablesで二重に評価されてしまう
検証コード
- 1. 以下の様なアクション定義と、それに対応するアクションクラスを用意する。
<result type="httpheader"> <param name="headers.foobar">${message}</param> </result>
- 2. このアクションを以下の様なURLでアクセスする。
http://localhost:8080/example/HelloWorld.action?message=%24{%25{1%2B2}} http://localhost:8080/example/HelloWorld.action?message=${%{1+2}}
- 3. HTTPヘッダのfoobarの値が3になっている。
修正内容
With the new version actions' names whitelisting was introduced and by default is set to accept actions that match the following regex:
[a-z]*[A-Z]*[0-9]*[.\-_!/]*
user can change the definition by setting up a new constant in struts.xml as below:
Double evaluation of passed expression was removed from OgnlTextParser which is used by TextParseUtil.translateVariables.
ホワイトリスト方式のアクション名を導入した。アクション名は以下の正規表現に一致したもののみ受け入れる。
[a-z]*[A-Z]*[0-9]*[.\-_!/]*
もしこの正規表現を変更したい場合は以下の通り。
OGNLを二重評価してしまう問題は、TextParseUtil#translateVariablesの委譲先であるOgnlTextParserを修正することで解消した。