Vulnerabilidad DoS por expansión de entidades en REXML

La expansión de entidades sin restricciones puede resultar en una vulnerabilidad de DoS en REXML.

A esta vulnerabilidad se le ha asignado el identificador CVE-2013-1821.

Les recomendamos actualizar Ruby cuanto antes.

Detalles

Cuando se leen nodos de texto de un documento XML, el parser de REXML puede ser forzado a poner en memoria objetos de cadena muy grandes que pueden terminar consumiendo toda la memoria de una computadora, causando DoS (negación del servicio).

Código afectado puede verse de esta manera:

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

Cuando se llama a el método `text`, las entidades se exapnden. Un atacante puede enviar un documento XML relativamente pequeño que cuando se intenten resolver sus entidades se consumirán cantidades excesivamente grandes de memoria en el sistema atacado.

Este ataque es similar pero diferente al ataque del billón de risas. Tambien está relacionado a la vulnerabilidad CVE-2013-1664 de Python.

Todos los usuarios con una versión afectada por esta entrega deberian actualizar o usar una versión de las soluciones inmediatamente.

Soluciones

Si no puedes actualizar Ruby, puedes utilizar este parche como solución temporal:

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

Este parche limitará el tamaño de las entidades a 10k por nodo. REXML ya tiene como limite predispuesto permitir solo 10000 substituciones de entidades por documento, asi que el tamaño máximo de texto que puede ser generado mientras se substituye una entidad será de aproximadamente 98 megabytes.

Versiones afectadas

  • Todas las versiones de ruby 1.9 anteriores a 1.9.3-p392
  • Todas las versiones de ruby 2.0 anteriores a 2.0.0-p0
  • Todo lo anterior a la revisión 39384

Historia

  • Agregar el numero CVE 2013-03-11 07:45:00 (UTC)
  • Publicado originalmente 2013-02-22 12:00:00 (UTC)