Moderate severityNVD Advisory· Published Apr 3, 2013· Updated Apr 29, 2026
CVE-2013-1664
CVE-2013-1664
Description
The XML libraries for Python 3.4, 3.3, 3.2, 3.1, 2.7, and 2.6, as used in OpenStack Keystone Essex, Folsom, and Grizzly; Compute (Nova) Essex and Folsom; Cinder Folsom; Django; and possibly other products allow remote attackers to cause a denial of service (resource consumption and crash) via an XML Entity Expansion (XEE) attack.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | >= 1.3.0, < 1.3.6 | 1.3.6 |
DjangoPyPI | >= 1.4.0, < 1.4.4 | 1.4.4 |
Affected products
6- cpe:2.3:a:openstack:cinder_folsom:-:*:*:*:*:*:*:*
- cpe:2.3:a:openstack:compute_\(nova\)_essex:-:*:*:*:*:*:*:*
- cpe:2.3:a:openstack:compute_\(nova\)_folsom:-:*:*:*:*:*:*:*
- cpe:2.3:a:openstack:keystone_essex:-:*:*:*:*:*:*:*
Patches
21c60d07ba23e[1.4.x] Restrict the XML deserializer to prevent network and entity-expansion DoS attacks.
2 files changed · +108 −1
django/core/serializers/xml_serializer.py+94 −1 modified@@ -8,6 +8,8 @@ from django.utils.xmlutils import SimplerXMLGenerator from django.utils.encoding import smart_unicode from xml.dom import pulldom +from xml.sax import handler +from xml.sax.expatreader import ExpatParser as _ExpatParser class Serializer(base.Serializer): """ @@ -149,9 +151,13 @@ class Deserializer(base.Deserializer): def __init__(self, stream_or_string, **options): super(Deserializer, self).__init__(stream_or_string, **options) - self.event_stream = pulldom.parse(self.stream) + self.event_stream = pulldom.parse(self.stream, self._make_parser()) self.db = options.pop('using', DEFAULT_DB_ALIAS) + def _make_parser(self): + """Create a hardened XML parser (no custom/external entities).""" + return DefusedExpatParser() + def next(self): for event, node in self.event_stream: if event == "START_ELEMENT" and node.nodeName == "object": @@ -290,3 +296,90 @@ def getInnerText(node): else: pass return u"".join(inner_text) + + +# Below code based on Christian Heimes' defusedxml + + +class DefusedExpatParser(_ExpatParser): + """ + An expat parser hardened against XML bomb attacks. + + Forbids DTDs, external entity references + + """ + def __init__(self, *args, **kwargs): + _ExpatParser.__init__(self, *args, **kwargs) + self.setFeature(handler.feature_external_ges, False) + self.setFeature(handler.feature_external_pes, False) + + def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): + raise DTDForbidden(name, sysid, pubid) + + def entity_decl(self, name, is_parameter_entity, value, base, + sysid, pubid, notation_name): + raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) + + def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) + + def external_entity_ref_handler(self, context, base, sysid, pubid): + raise ExternalReferenceForbidden(context, base, sysid, pubid) + + def reset(self): + _ExpatParser.reset(self) + parser = self._parser + parser.StartDoctypeDeclHandler = self.start_doctype_decl + parser.EntityDeclHandler = self.entity_decl + parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl + parser.ExternalEntityRefHandler = self.external_entity_ref_handler + + +class DefusedXmlException(ValueError): + """Base exception.""" + def __repr__(self): + return str(self) + + +class DTDForbidden(DefusedXmlException): + """Document type definition is forbidden.""" + def __init__(self, name, sysid, pubid): + super(DTDForbidden, self).__init__() + self.name = name + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class EntitiesForbidden(DefusedXmlException): + """Entity definition is forbidden.""" + def __init__(self, name, value, base, sysid, pubid, notation_name): + super(EntitiesForbidden, self).__init__() + self.name = name + self.value = value + self.base = base + self.sysid = sysid + self.pubid = pubid + self.notation_name = notation_name + + def __str__(self): + tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class ExternalReferenceForbidden(DefusedXmlException): + """Resolving an external reference is forbidden.""" + def __init__(self, context, base, sysid, pubid): + super(ExternalReferenceForbidden, self).__init__() + self.context = context + self.base = base + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})" + return tpl.format(self.sysid, self.pubid)
tests/regressiontests/serializers_regress/tests.py+14 −0 modified@@ -16,6 +16,7 @@ from cStringIO import StringIO except ImportError: from StringIO import StringIO +from django.core.serializers.xml_serializer import DTDForbidden try: import yaml @@ -523,3 +524,16 @@ def streamTest(format, self): if format != 'python': setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) + +class XmlDeserializerSecurityTests(TestCase): + + def test_no_dtd(self): + """ + The XML deserializer shouldn't allow a DTD. + + This is the most straightforward way to prevent all entity definitions + and avoid both external entities and entity-expansion attacks. + + """ + xml = '<?xml version="1.0" standalone="no"?><!DOCTYPE example SYSTEM "http://example.com/example.dtd">' + self.assertRaises(DTDForbidden, serializers.deserialize('xml', xml).next)
d19a27066b22[1.3.x] Restrict the XML deserializer to prevent network and entity-expansion DoS attacks.
2 files changed · +108 −1
django/core/serializers/xml_serializer.py+93 −1 modified@@ -8,6 +8,8 @@ from django.utils.xmlutils import SimplerXMLGenerator from django.utils.encoding import smart_unicode from xml.dom import pulldom +from xml.sax import handler +from xml.sax.expatreader import ExpatParser as _ExpatParser class Serializer(base.Serializer): """ @@ -154,9 +156,13 @@ class Deserializer(base.Deserializer): def __init__(self, stream_or_string, **options): super(Deserializer, self).__init__(stream_or_string, **options) - self.event_stream = pulldom.parse(self.stream) + self.event_stream = pulldom.parse(self.stream, self._make_parser()) self.db = options.pop('using', DEFAULT_DB_ALIAS) + def _make_parser(self): + """Create a hardened XML parser (no custom/external entities).""" + return DefusedExpatParser() + def next(self): for event, node in self.event_stream: if event == "START_ELEMENT" and node.nodeName == "object": @@ -295,3 +301,89 @@ def getInnerText(node): else: pass return u"".join(inner_text) + + +# Below code based on Christian Heimes' defusedxml + + +class DefusedExpatParser(_ExpatParser): + """ + An expat parser hardened against XML bomb attacks. + + Forbids DTDs, external entity references + + """ + def __init__(self, *args, **kwargs): + _ExpatParser.__init__(self, *args, **kwargs) + self.setFeature(handler.feature_external_ges, False) + self.setFeature(handler.feature_external_pes, False) + + def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): + raise DTDForbidden(name, sysid, pubid) + + def entity_decl(self, name, is_parameter_entity, value, base, + sysid, pubid, notation_name): + raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) + + def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) + + def external_entity_ref_handler(self, context, base, sysid, pubid): + raise ExternalReferenceForbidden(context, base, sysid, pubid) + + def reset(self): + _ExpatParser.reset(self) + parser = self._parser + parser.StartDoctypeDeclHandler = self.start_doctype_decl + parser.EntityDeclHandler = self.entity_decl + parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl + parser.ExternalEntityRefHandler = self.external_entity_ref_handler + + +class DefusedXmlException(ValueError): + """Base exception.""" + def __repr__(self): + return str(self) + + +class DTDForbidden(DefusedXmlException): + """Document type definition is forbidden.""" + def __init__(self, name, sysid, pubid): + self.name = name + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class EntitiesForbidden(DefusedXmlException): + """Entity definition is forbidden.""" + def __init__(self, name, value, base, sysid, pubid, notation_name): + super(EntitiesForbidden, self).__init__() + self.name = name + self.value = value + self.base = base + self.sysid = sysid + self.pubid = pubid + self.notation_name = notation_name + + def __str__(self): + tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class ExternalReferenceForbidden(DefusedXmlException): + """Resolving an external reference is forbidden.""" + def __init__(self, context, base, sysid, pubid): + super(ExternalReferenceForbidden, self).__init__() + self.context = context + self.base = base + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})" + return tpl.format(self.sysid, self.pubid)
tests/regressiontests/serializers_regress/tests.py+15 −0 modified@@ -14,6 +14,7 @@ from cStringIO import StringIO except ImportError: from StringIO import StringIO +from django.core.serializers.xml_serializer import DTDForbidden from django.conf import settings from django.core import serializers, management @@ -416,3 +417,17 @@ def streamTest(format, self): setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format)) if format != 'python': setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) + + +class XmlDeserializerSecurityTests(TestCase): + + def test_no_dtd(self): + """ + The XML deserializer shouldn't allow a DTD. + + This is the most straightforward way to prevent all entity definitions + and avoid both external entities and entity-expansion attacks. + + """ + xml = '<?xml version="1.0" standalone="no"?><!DOCTYPE example SYSTEM "http://example.com/example.dtd">' + self.assertRaises(DTDForbidden, serializers.deserialize('xml', xml).next)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
14- bugs.launchpad.net/nova/+bug/1100282nvdExploitWEB
- lists.openstack.org/pipermail/openstack-announce/2013-February/000078.htmlnvdVendor AdvisoryWEB
- github.com/advisories/GHSA-qrh7-x6fp-c2mpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-1664ghsaADVISORY
- blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.htmlnvdWEB
- bugs.python.org/issue17239nvdWEB
- rhn.redhat.com/errata/RHSA-2013-0657.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2013-0658.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2013-0670.htmlnvdWEB
- ubuntu.com/usn/usn-1757-1nvdWEB
- www.openwall.com/lists/oss-security/2013/02/19/2nvdWEB
- www.openwall.com/lists/oss-security/2013/02/19/4nvdWEB
- github.com/django/django/commit/1c60d07ba23e0350351c278ad28d0bd5aa410b40ghsaWEB
- github.com/django/django/commit/d19a27066b2247102e65412aa66917aff0091112ghsaWEB
News mentions
0No linked articles in our index yet.