Vulnerabilità DoS di espansione di entità in REXML (XML bomb, CVE-2013-1821)

Un'incontrollata espansione di entità può provocare una vulnerabilità DoS in REXML. L'identificativo CVE "CVE-2013-1821" è stato assegnato a questa vulnerabilità. Raccomandiamo fortemente di aggiornare Ruby.

Dettagli

Durante la lettura di nodi testuali da un documento XML, il parser RAXML può essere utilizzato per allocare degli oggetti string estremamente grandi che possono consumare tutta la memoria su una macchina, causando un Denial of Service.

Il codice impattato è del tipo:

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

Quando il metodo `text` viene chiamato, le entità XML vengono espanse. Un utente malintenzionato può utilizzare un documento XML relativamente piccolo che, quando le entità vengono risolte, consumerà grandi quantità di memoria nel sistema.

Da notare che questo attacco è simile, ma differente, all'attacco "Billion Laughs", che è correlato al CVE-2013-1664 di Python.

Tutti gli utenti che utilizzano una versione soggetta a questa vulnerabilità dovrebbero procedere all'aggiornamento o utilizzare uno dei workaround immediatamente.

Workaround

Se non puoi aggiornare Ruby, utilizza questa patch manuale come workaround:

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

Questa patch limiterà la grandezza della sostituzione delle entità a 10k per nodo. REXML inoltre di default permette solamente 10000 sostituzioni di entità per documento, quindi il massimo ammontare di testo che può essere generato tramite sostituzione di entità sarà attorno ai 98MB.

Versioni soggette alla vulnerabilità

  • Tutte le versioni di Ruby 1.9 precedenti a 1.9.3 patchlevel 392
  • Tutte le versioni di Ruby 2.0 precedenti a 2.0.0 patchlevel 0
  • Tutte le versioni precedenti alla trunk revision 39384

Crediti

Grazie a Ben Murphy per averci notificato il problema.

Cronologia

  • Originariamente pubblicato il giorno 2013-02-22 alle ore 12:00:00 (UTC)