Podatność na DoS w REXML przez rozrost encji (bomba XML, CVE-2013-1821)

Nieograniczony rozrost encji może prowadzić do podatności na DoS w REXML. Tej podatności został przypisany identyfikator CVE-2013-1821. Zdecydowanie zalecamy aktualizację Rubiego.

Szczegóły

Podczas czytania węzłów tekstu z dokumentu XML, parser REXML może zostać zmuszony do alokowania bardzo dużych napisów, które mogą zająć całą dostępną pamięć maszyny, powodując odmowę usługi.

Przykładowy kod ilustrujący lukę będzie wyglądać mniej więcej tak:

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

Kiedy jest wołana metoda `text` , encje będą się rozrastać. Atakujący może wysłać relatywnie mały dokument XML, który kiedy encje są wiązane, będzie zajmował całą dostępną pamięć docelowego systemu.

Zauważ, że ten atak jest podobny do, ale różny od ataku Billion Laughs. Jest to też powiązane z CVE-2013-1664 dla Pythona.

Wszyscy użytkownicy używający podatnej wersji powinni natychmiast zaktualizować ją lub użyć obejścia problemu.

Obejście problemu

Gdy nie możesz zaktualizować Rubiego, ta łatka - monkey patch - może być użyta jako obejście problemu:

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

Ta łatka wprowadzi limit na rozmiar podstawianej encji do 10k na węzeł. Obecnie REXML domyślnie dopuszcza jedynie 10000 podstawień encji na dokument, więc maksymalna ilość tekstu, który może być wygenerowany będzie wynosił około 98 megabajtów.

Dotyczy wersji

  • Dla 1.9 wszystkie wcześniejsze wersje od 1.9.3 patchlevel 392
  • Dla 2.0 wszystkie wcześniejsze wersje od 2.0.0 patchlevel 0
  • Wcześniej niż rewizja trunk 39384

Podziękowania

Podziękowania dla Bena Murphy’ego za zgłoszenie tego problemu.

Historia

  • Dodano numer CVE 2013-03-11 07:45:00 (UTC)
  • Oryginalnie opublikowane 2013-02-22 12:00:00 (UTC)