REXML におけるエンティティ展開に伴うサービス不能攻撃について (CVE-2013-1821)

REXML において、厳格でないエンティティ展開により、サービス不能攻撃が可能となる脆弱性が報告されました。 この脆弱性は CVE-2013-1821 として CVE に登録されています。 ユーザーの皆さんには ruby を更新することを強くお勧めします。

詳細

XML ドキュメントからテキストのノードを読み込む際、REXML のパーサに極めて巨大な String オブジェクトを生成させ、マシン上のメモリを使い果たさせてサービス不能攻撃を成立させることができます。

影響を受けるのは以下のようなコードです:

document = REXML::Document.new some_xml_doc
document.root.text

`text` メソッドが呼ばれる際、XML のエンティティが展開されます。 攻撃者の送信する XML ドキュメントが比較的小さなものであったとしても、そのエンティティが解決される際には対象システム上で非常に大量のメモリを消費させることができます。

この攻撃は「Billion Laughs」攻撃として知られているものに似ていますが、それとは別のものです。 また、この脆弱性は Python について報告されている CVE-2013-1664 とも関連しています。

影響を受けるバージョンの ruby を使用している全てのユーザーは、速やかに、ruby を更新するか、以下の回避策を適用して下さい。

回避策

もし ruby を更新できない場合、回避策として以下のモンキーパッチを適用して下さい:

class REXML::Document
  @@entity_expansion_text_limit = 10_240

  def self.entity_expansion_text_limit=( val )
    @@entity_expansion_text_limit = val
  end

  def self.entity_expansion_text_limit
    @@entity_expansion_text_limit
  end
end

class REXML::Text
  def self.unnormalize(string, doctype=nil, filter=nil, illegal=nil)
    sum = 0
    string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) {
      s = self.expand($&, doctype, filter)
      if sum + s.bytesize > REXML::Document.entity_expansion_text_limit
        raise "entity expansion has grown too large"
      else
        sum += s.bytesize
      end
      s
    }
  end

  def self.expand(ref, doctype, filter)
    if ref[1] == ?#
      if ref[2] == ?x
        [ref[3...-1].to_i(16)].pack('U*')
      else
        [ref[2...-1].to_i].pack('U*')
      end
    elsif ref == '&'
      '&'
    elsif filter and filter.include?( ref[1...-1] )
      ref
    elsif doctype
      doctype.entity( ref[1...-1] ) or ref
    else
      entity_value = DocType::DEFAULT_ENTITIES[ ref[1...-1] ]
      entity_value ? entity_value.value : ref
    end
  end
end

このモンキーパッチは、エンティティ置換のサイズを 1 ノードあたり 10Kb に制限します。 REXML は元々デフォルトでは 1 ドキュメントにつき 10000 エンティティの置換しか許可していないので、この制限を追加することにより、エンティティ置換で生成されるテキストの最大サイズは 98 メガバイト前後までになります。

影響を受けるバージョン

  • ruby 1.9.3 patchlevel 392 より前の全ての ruby 1.9 系列
  • ruby 2.0.0 patchlevel 0 より前の全ての ruby 2.0 系列
  • revision 39384 より前の開発版 (trunk)

クレジット

この脆弱性は Ben Murphy によって報告されました。

更新履歴

  • 2013-03-11 16:45:00 (JST) CVE 番号について追記
  • 2013-02-22 21:00:00 (JST) 初版