JPCERT コーディネーションセンター

SEC03-J. 信頼できないコードに任意のクラスのロードを許可した後で信頼するクラスをロードしない

プログラムが使用するJavaのクラスは、必ずしもプログラムのスタートアップ時にロードされるとは限らない。多くのJava仮想マシン(JVM)では必要になって初めてクラスをロードする。

信頼できないコードにクラスのロードを許すと、信頼できないコードは、細工されたクラスをロードできるようになる。ここで細工されたクラスは、信頼するコードが必要とする無害なクラスと同じ完全修飾名を持つクラスである。信頼するコードが、無害なクラスをロードしようとすると、JVM は細工されたクラスを渡す。したがって、信頼できないコードにクラスのロードを許可するのであれば、プログラムは、必要とする無害なクラスをすべて先にロード(preload)しなくてはならない。いったんクラスを適切に読み込めば、読み込まれた無害なクラスが信頼できないコードによって置き換えられることはない。

違反コード (Tomcat)

以下に、Tomcat HTTP ウェブサーバの複数のバージョンに発見された脆弱性を示す(バージョン 6.0.20 で修正されている)。この脆弱性により、システムが使用するデフォルトのXMLパーサを、信頼できないウェブアプリケーションが上書きすることが可能であった。このXMLパーサは、Tomcat のインスタンス上に配備されているウェブアプリケーションの、web.xmlcontext.xml、タグライブラリディスクリプタ(TLD)ファイルを処理するためのものである。したがって、悪意あるパーサをインストールする信頼できないウェブアプリケーションは、ある状況において、これらのファイルの内容を読み取ったり変更することができたのである。

以下の違反コード例に、org.apache.catalina.startup.ContextConfig クラスの Digester インスタンスの初期化に関連するコードを示す。「Digester は、XML 入力ストリームの中から、XML エレメントの一定の入れ子状態のパターンを検出し、それに対して Rule を実行する。この Rule は、パース処理を始める前に追加しておいたものである」[Tomcat 2009]。Digester を初期化するコードは以下の通りである。

protected static Digester webDigester = null;

if (webDigester == null) {
  webDigester = createWebDigester();
}

createWebDigester メソッドは Digester インスタンスを作成する役割を担う。このメソッドは createWebXMLDigester() メソッドを呼び出し、createWebXMLDigester()DigesterFactory.newDigester() を呼び出す。DigesterFactory.newDigester() メソッドは、新規インスタンスを作成し、boolean フラグである useContextClassLoadertrue を代入する。

// このメソッドは DigesterFactory クラスに定義されており、
// ContextConfig.createWebXmlDigester() から呼び出される。
// ContextConfig.createWebXmlDigester() は
// ContextConfig.createWebDigester() から呼び出される。
// webDigester は最終的にこのメソッドで定義された digester の値を保持する。
public static Digester newDigester(boolean xmlValidation,
                                   boolean xmlNamespaceAware,
                                   RuleSet rule) {
  Digester digester = new Digester();
  // ...
  digester.setUseContextClassLoader(true);
  // ...
  return digester;
}

DigesteruseContextClassLoader フラグを使って、新規クラスをロードする際に使用する ClassLoader を決定する。このフラグが true である場合、WebappClassLoader を使用する。WebappClassLoader は信頼できないクラスである。なぜなら、様々な web アプリケーションがリクエストするクラスをリクエストされるがままにロードするからである。

public ClassLoader getClassLoader() {
  // ...
  if (this.useContextClassLoader) {
    // WebappClassLoader に以前設定されたクラスコンテキストローダを使用する
    ClassLoader classLoader =
        Thread.currentThread().getContextClassLoader();
  }
  return classloader;
}

次に、Digester.getParser() メソッドが Tomcat から呼び出され、web.xml や他のファイルを処理する。

// Digester.getParser() はこのメソッドを呼び出す。このメソッドは Digester クラスに定義されている。
public SAXParserFactory getFactory() {
  if (factory == null) {
    factory = SAXParserFactory.newInstance(); // WebappClassLoader を使う
    // ...
  }
  return (factory);
}

根本的な問題は、ウェブアプリケーションのクラスローダである WebappClassLoader の代わりに newInstance() メソッドが呼び出されており、それによって、Tomcat が必要とするすべてのクラスをロードする前に、いくつかのクラスがロードされることにある。もしウェブアプリケーションがトロイの木馬として独自の javax.xml.parsers.SAXParserFactory をロードしていると、Tomcat が SAXParserFactory にアクセスしようとしたときに、Java 標準の SAXParserFactory ではなく、ウェブアプリケーションによってロードされたトロイの木馬である SaxParserFactory にアクセスしてしまう。

適合コード (Tomcat)

以下の適合コードでは、Tomcat は Digester の作成時に SAXParserFactory を初期化している。こうすることで、WebappClassLoader ではなく、コンテナのクラスローダを使用して SAXParserFactory が構築されることが保証される。

また、webDigesterfinal 宣言されており、サブクラスが新規のオブジェクト参照を webDigester に割り当てられないようにしている(詳しくは「OBJ10-J. public static 変数を final 宣言せずに使わない」を参照)。 さらに、webDigester の初期化が完了する前に他のスレッドがアクセスできるような競合の発生も防いでいる(詳しくは「OBJ11-J. コンストラクタが例外をスローする場合には細心の注意を払う」を参照)。

protected static final Digester webDigester = init();

protected Digester init() {
  Digester digester = createWebDigester();
  // 初期化時にコンテキストクラスローダを使用しない
  digester.getParser(); 
  return digester;
}

Tomcat サーバが、web.xml やその他のファイルを処理しようとする際に、パーサインスタンスを作成するために WebappClassLoader を使用し続けたとしても、init() の中で明示的に getParser() を呼び出すことで、初期化が完了する前にデフォルトのパーサが設定されることが保証され、パーサが置き換えられることを防いでいる。設定は一度しか行えないので、将来さらにパーサを変更しようとしても変更できない。

Class.newInstance() メソッドは、クラスが引数無しのコンストラクタを持つことを要求することに注意。この要求が満たされない場合には実行時例外が発生し、情報漏えいを間接的に防止する。

リスク評価

信頼できないコードがクラスをロードすることを許すと、正常なクラスをトロイの木馬となるクラスに置き換えられてしまう危険がある。

ルール 深刻度 可能性 修正コスト 優先度 レベル
SEC03-J P12 L1
関連ガイドライン
Secure Coding Guidelines for the Java Programming Language, Version 3.0 Guideline 6-3. Safely invoke standard APIs that bypass SecurityManager checks depending on the immediate caller's class loader
参考文献
[CVE 2011] CVE-2009-0783
[Gong 2003] Section 4.3.2, Class Loader Delegation Hierarchy
[JLS 2005] §4.3.2, The Class Object
[Tomcat 2009] Bug ID 29936, API Class org.apache.tomcat.util.digester.Digester, Security fix in v 6.0.20
翻訳元

これは以下のページを翻訳したものです。

SEC03-J. Do not load trusted classes after allowing untrusted code to load arbitrary classes (revision 119)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter