From a5c0ffa99a99232cee1f06edd49b56c646670d8f Mon Sep 17 00:00:00 2001 From: Alan Pevec Date: Mar 16 2017 23:03:43 +0000 Subject: Fixing CVE-2016-10149: Entity expansion issue Resolves: CVE-2016-10149 --- diff --git a/python-pysaml2-3.0.2-CVE-2016-10149.patch b/python-pysaml2-3.0.2-CVE-2016-10149.patch new file mode 100644 index 0000000..000b5ab --- /dev/null +++ b/python-pysaml2-3.0.2-CVE-2016-10149.patch @@ -0,0 +1,251 @@ +diff -r -u pysaml2-3.0.2/setup.py pysaml2-3.0.2-CVE-2016-10149/setup.py +--- pysaml2-3.0.2/setup.py 2015-09-03 00:55:42.000000000 -0400 ++++ pysaml2-3.0.2-CVE-2016-10149/setup.py 2017-03-16 17:43:54.865077102 -0400 +@@ -32,6 +32,7 @@ + 'pytz', + 'pyOpenSSL', + 'python-dateutil', ++ 'defusedxml', + 'six' + ] + +diff -r -u pysaml2-3.0.2/src/saml2/__init__.py pysaml2-3.0.2-CVE-2016-10149/src/saml2/__init__.py +--- pysaml2-3.0.2/src/saml2/__init__.py 2015-11-03 10:49:04.000000000 -0500 ++++ pysaml2-3.0.2-CVE-2016-10149/src/saml2/__init__.py 2017-03-16 17:43:54.865077102 -0400 +@@ -35,6 +35,7 @@ + import cElementTree as ElementTree + except ImportError: + from elementtree import ElementTree ++import defusedxml.ElementTree + + root_logger = logging.getLogger(__name__) + root_logger.level = logging.NOTSET +@@ -86,7 +87,7 @@ + """ + if not isinstance(xml_string, six.binary_type): + xml_string = xml_string.encode('utf-8') +- tree = ElementTree.fromstring(xml_string) ++ tree = defusedxml.ElementTree.fromstring(xml_string) + return create_class_from_element_tree(target_class, tree) + + +@@ -268,7 +269,7 @@ + + + def extension_element_from_string(xml_string): +- element_tree = ElementTree.fromstring(xml_string) ++ element_tree = defusedxml.ElementTree.fromstring(xml_string) + return _extension_element_from_element_tree(element_tree) + + +diff -r -u pysaml2-3.0.2/src/saml2/pack.py pysaml2-3.0.2-CVE-2016-10149/src/saml2/pack.py +--- pysaml2-3.0.2/src/saml2/pack.py 2015-10-15 15:30:21.000000000 -0400 ++++ pysaml2-3.0.2-CVE-2016-10149/src/saml2/pack.py 2017-03-16 17:43:54.865077102 -0400 +@@ -35,6 +35,7 @@ + import cElementTree as ElementTree + except ImportError: + from elementtree import ElementTree ++import defusedxml.ElementTree + + NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" + FORM_SPEC = """
+@@ -211,7 +212,7 @@ + :param text: The SOAP object as XML + :return: header parts and body as saml.samlbase instances + """ +- envelope = ElementTree.fromstring(text) ++ envelope = defusedxml.ElementTree.fromstring(text) + assert envelope.tag == '{%s}Envelope' % NAMESPACE + + #print(len(envelope)) +diff -r -u pysaml2-3.0.2/src/saml2/soap.py pysaml2-3.0.2-CVE-2016-10149/src/saml2/soap.py +--- pysaml2-3.0.2/src/saml2/soap.py 2015-05-18 03:54:05.000000000 -0400 ++++ pysaml2-3.0.2-CVE-2016-10149/src/saml2/soap.py 2017-03-16 17:43:54.866077073 -0400 +@@ -19,6 +19,7 @@ + except ImportError: + #noinspection PyUnresolvedReferences + from elementtree import ElementTree ++import defusedxml.ElementTree + + + logger = logging.getLogger(__name__) +@@ -133,7 +134,7 @@ + :param expected_tags: What the tag of the SAML thingy is expected to be. + :return: SAML thingy as a string + """ +- envelope = ElementTree.fromstring(text) ++ envelope = defusedxml.ElementTree.fromstring(text) + + # Make sure it's a SOAP message + assert envelope.tag == '{%s}Envelope' % soapenv.NAMESPACE +@@ -183,7 +184,7 @@ + :return: The body and headers as class instances + """ + try: +- envelope = ElementTree.fromstring(text) ++ envelope = defusedxml.ElementTree.fromstring(text) + except Exception as exc: + raise XmlParseError("%s" % exc) + +@@ -209,7 +210,7 @@ + :return: dictionary with two keys "body"/"header" + """ + try: +- envelope = ElementTree.fromstring(text) ++ envelope = defusedxml.ElementTree.fromstring(text) + except Exception as exc: + raise XmlParseError("%s" % exc) + +diff -r -u pysaml2-3.0.2/tests/test_03_saml2.py pysaml2-3.0.2-CVE-2016-10149/tests/test_03_saml2.py +--- pysaml2-3.0.2/tests/test_03_saml2.py 2015-06-06 03:15:20.000000000 -0400 ++++ pysaml2-3.0.2-CVE-2016-10149/tests/test_03_saml2.py 2017-03-16 17:43:54.866077073 -0400 +@@ -17,6 +17,7 @@ + import cElementTree as ElementTree + except ImportError: + from elementtree import ElementTree ++from defusedxml.common import EntitiesForbidden + + ITEMS = { + NameID: [""" +@@ -166,6 +167,19 @@ + assert kl == None + + ++def test_create_class_from_xml_string_xxe(): ++ xml = """ ++ ++ ++ ++ ]> ++ &lol1; ++ """ ++ with raises(EntitiesForbidden) as err: ++ create_class_from_xml_string(NameID, xml) ++ ++ + def test_ee_1(): + ee = saml2.extension_element_from_string( + """bar""") +@@ -454,6 +468,19 @@ + assert nid.text.strip() == "http://federationX.org" + + ++def test_ee_xxe(): ++ xml = """ ++ ++ ++ ++ ]> ++ &lol1; ++ """ ++ with raises(EntitiesForbidden): ++ saml2.extension_element_from_string(xml) ++ ++ + def test_extension_element_loadd(): + ava = {'attributes': {}, + 'tag': 'ExternalEntityAttributeAuthority', +diff -r -u pysaml2-3.0.2/tests/test_43_soap.py pysaml2-3.0.2-CVE-2016-10149/tests/test_43_soap.py +--- pysaml2-3.0.2/tests/test_43_soap.py 2013-04-28 10:38:07.000000000 -0400 ++++ pysaml2-3.0.2-CVE-2016-10149/tests/test_43_soap.py 2017-03-16 17:43:54.866077073 -0400 +@@ -12,9 +12,13 @@ + import cElementTree as ElementTree + except ImportError: + from elementtree import ElementTree ++from defusedxml.common import EntitiesForbidden ++ ++from pytest import raises + + import saml2.samlp as samlp + from saml2.samlp import NAMESPACE as SAMLP_NAMESPACE ++from saml2 import soap + + NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" + +@@ -66,3 +70,42 @@ + assert len(body) == 1 + saml_part = body[0] + assert saml_part.tag == '{%s}AuthnRequest' % SAMLP_NAMESPACE ++ ++ ++def test_parse_soap_enveloped_saml_thingy_xxe(): ++ xml = """ ++ ++ ++ ++ ]> ++ &lol1; ++ """ ++ with raises(EntitiesForbidden): ++ soap.parse_soap_enveloped_saml_thingy(xml, None) ++ ++ ++def test_class_instances_from_soap_enveloped_saml_thingies_xxe(): ++ xml = """ ++ ++ ++ ++ ]> ++ &lol1; ++ """ ++ with raises(soap.XmlParseError): ++ soap.class_instances_from_soap_enveloped_saml_thingies(xml, None) ++ ++ ++def test_open_soap_envelope_xxe(): ++ xml = """ ++ ++ ++ ++ ]> ++ &lol1; ++ """ ++ with raises(soap.XmlParseError): ++ soap.open_soap_envelope(xml) +diff -r -u pysaml2-3.0.2/tests/test_51_client.py pysaml2-3.0.2-CVE-2016-10149/tests/test_51_client.py +--- pysaml2-3.0.2/tests/test_51_client.py 2015-10-17 02:48:08.000000000 -0400 ++++ pysaml2-3.0.2-CVE-2016-10149/tests/test_51_client.py 2017-03-16 17:46:23.732727731 -0400 +@@ -21,6 +21,7 @@ + from saml2.authn_context import INTERNETPROTOCOLPASSWORD + from saml2.client import Saml2Client + from saml2.config import SPConfig ++from saml2.pack import parse_soap_enveloped_saml + from saml2.response import LogoutResponse + from saml2.saml import NAMEID_FORMAT_PERSISTENT, EncryptedAssertion, Advice + from saml2.saml import NAMEID_FORMAT_TRANSIENT +@@ -34,9 +35,12 @@ + from saml2.s_utils import factory + from saml2.time_util import in_a_while, a_while_ago + ++from defusedxml.common import EntitiesForbidden ++ + from fakeIDP import FakeIDP + from fakeIDP import unpack_form + from pathutils import full_path ++from pytest import raises + + AUTHN = { + "class_ref": INTERNETPROTOCOLPASSWORD, +@@ -1448,6 +1452,17 @@ + 'http://www.example.com/login' + assert ac.authn_context_class_ref.text == INTERNETPROTOCOLPASSWORD + ++def test_parse_soap_enveloped_saml_xxe(): ++ xml = """ ++ ++ ++ ++ ]> ++ &lol1; ++ """ ++ with raises(EntitiesForbidden): ++ parse_soap_enveloped_saml(xml, None) + + # if __name__ == "__main__": + # tc = TestClient()