Суть DoS уязвимости в REXML (XML bomb, CVE-2013-1821)

Неограниченное расширение сущности может привести к DoS уязвимости в REXML. Этой уязвимости присвоен CVE идентификатор CVE-2013-1821. Мы строго рекомендуем обновить ruby.

Детали

Во время чтения текстовых нод XML документа, парсер REXML может быть вынужден к резервированию экстремально больших строковых объектов, которые могут растратить всю память машины, вызвав недоступность сервиса (DoS).

Опасный код может выглядеть как-то так:

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

Когда вызывается метод `text`, сущности раскрываются. Атакующий может послать относительно небольшой XML документ так, что когда сущности будут раскрываться, их вложенности будут потреблять экстремально большое количество памяти атакованной системы.

Заметьте, что данная атака похожа на Billion Laughs атаки, хотя и имеет некоторые отличия. Это также относится к CVE-2013-1664 уязвимости Python.

Все пользователи, использующие затронутые релизы, должны либо обновиться, либо использовать один из обходных путей.

Обходные пути

Если вы не можете обновить 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

Этот манкипатч ограничивает размер вложенностей каждой сущности до 10k на ноду. REXML уже по умолчанию настроен на 10000 вложенностей сущности на документ, так что максимальное количество текста, который может быть сгенерирован из вложенностей сущностей должен быть около 98 мегабайт.

Затронутые сущности

  • Все версии ruby 1.9 до ruby 1.9.3 patchlevel 392
  • Все версии ruby 2.0 до ruby 2.0.0 patchlevel 0
  • транк до ревизии 39384

Благодарности

Спасибо Ben Murphy за сообщение о данной проблеме.

История

  • Добавлено о CVE номере 2013-03-11 07:45:00 (UTC)
  • Оригинал опубликован 2013-02-22 12:00:00 (UTC)