Posted by usa on 22 Feb 2013
Unrestricted entity expansion can lead to a DoS vulnerability in REXML. This vulnerability has been assigned the CVE identifier CVE-2013-1821. We strongly recommend to upgrade ruby.
Details
When reading text nodes from an XML document, the REXML parser can be coerced in to allocating extremely large string objects which can consume all of the memory on a machine, causing a denial of service.
Impacted code will look something like this:
document = REXML::Document.new some_xml_doc
document.root.text
When the `text` method is called, entities will be expanded. An attacker can send a relatively small XML document that, when the entities are resolved, will consume extreme amounts of memory on the target system.
Note that this attack is similar to, but different from the Billion Laughs attack. This is also related to CVE-2013-1664 of Python.
All users running an affected release should either upgrade or use one of the workarounds immediately.
Workarounds
If you cannot upgrade Ruby, use this monkey patch as a 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
This monkey patch will limit the size of the entity substitutions to 10k per node. REXML already defaults to only allow 10000 entity substitutions per document, so the maximum amount of text that can be generated by entity substitution will be around 98 megabytes.
Affected versions
- All ruby 1.9 versions prior to ruby 1.9.3 patchlevel 392
- All ruby 2.0 versions prior to ruby 2.0.0 patchlevel 0
- prior to trunk revision 39384
Credits
Thanks to Ben Murphy for reporting this issue.
History
- Added about CVE number at 2013-03-11 07:45:00 (UTC)
- Originally published at 2013-02-22 12:00:00 (UTC)