CVE-2020-5390 (published 2020-01-13T19:15Z)

PySAML2 before 5.0.0 does not check that the signature in a SAML document is enveloped and thus signature wrapping is effective, i.e., it is affected by XML Signature Wrapping (XSW). The signature information and the node/object that is signed can be in different places and thus the signature verification will succeed, but the wrong data will be used. This specifically affects the verification of assertion that have been signed.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-347
cwe_name: Improper Verification of Cryptographic Signature
description: The product does not verify, or incorrectly verifies, the cryptographic signature for data.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/IdentityPython/pysaml2
commit_details: repo_url: https://github.com/IdentityPython/pysaml2
author: Ivan Kanakarakis
author_date: 2020-01-13 11:37:28+02:00
msg: Release version 5.0.0

Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
repository: repo_url: https://github.com/IdentityPython/pysaml2
repo_name: IdentityPython/pysaml2
description: Python implementation of SAML2
repo_language: Python
owner: IdentityPython
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,26 @@
 # Changelog
 
+## 5.0.0 (2020-01-13)
+
+- Fix XML Signature Wrapping (XSW) vulnerabilities - CVE-2020-5390
+- Add freshness period feature for MetaDataMDX
+- Fix bug in duration calculation in time_util library
+- Fix ipv6 validation to accommodate for addresses with brackets
+- Fix xmlsec temporary files deletions
+- Add method to get supported algorithms from metadata
+- Add mdstore method to extract assurance certifications
+- Add mdstore method to extract contact_person data
+- Add attribute mappings from the Swiss eduPerson Schema
+- Make AESCipher and Fernet interfaces compatible
+- Remove deprecated saml2.aes module
+- Remove deprecated saml2.extensions.ui module
+- Replace deprecated mongodb operations
+- Rename ToOld error to TooOld
+- Fix pytest warnings
+- Mock tests that need a network connection
+- Start dropping python2 support
+
+
 ## 4.9.0 (2019-11-03)
 
 - Add mdstore methods to extract mdui uiinfo elements

programming_language: Markdown
filename: VERSION
change_type: ModificationType.MODIFY
diff: @@ -1 +1 @@
-4.9.0
+5.0.0

programming_language: SQL
repo_url: https://github.com/IdentityPython/pysaml2
commit_details: repo_url: https://github.com/IdentityPython/pysaml2
author: Ivan Kanakarakis
author_date: 2020-01-04 00:39:47+02:00
msg: Fix XML Signature Wrapping (XSW) vulnerabilities

PySAML2 did not check that the signature in a SAML document is enveloped and thus
XML signature wrapping (XSW) was effective.

The signature information and the node/object that is signed can be in different places
and thus the signature verification will succeed, but the wrong data will be used. This
specifically affects the verification of assertions that have been signed.

This was assigned CVE-2020-5390

Thanks to Alexey Sintsov and Yuri Goltsev from HERE Technologies to report this.

+ + + + + + + +

In more detail:

libxml2 follows the xmldsig-core specification. The xmldsig specification is way too
general. saml-core reuses the xmldsig specification, but constrains it to use of
specific facilities. The implementation of the SAML specification is responsible to
enforce those constraints. libxml2/xmlsec1 are not aware of those constraints and thus
process the document based on the full/general xmldsig rules.

What is happening is the following:

- xmldsig-core allows the signature-information and the data that was signed to be in
  different places. This works by setting the URI attribute of the Reference element.
  The URI attribute contains an optional identifier of the object being signed. (see
  "4.4.3 The Reference Element" -- https://www.w3.org/TR/xmldsig-core1/#sec-Reference)
  This identifier is actually a pointer that can be defined in many different ways; from
  XPath expressions that need to be executed(!), to a full URL that should be fetched(!)
  in order to recalculate the signature.

- saml-core section "5.4 XML Signature Profile" defines constrains on the xmldsig-core
  facilities. It explicitly dictates that enveloped signatures are the only signatures
  allowed. This mean that:
  * Assertion/RequestType/ResponseType elements must have an ID attribute
  * signatures must have a single Reference element
  * the Reference element must have a URI attribute
  * the URI attribute contains an anchor
  * the anchor points to the enclosing element's ID attribute

xmlsec1 does the right thing - it follows the reference URI pointer and validates the
assertion. But, the pointer points to an assertion in another part of the document; not
the assertion in which the signature is embedded/enveloped. SAML processing thinks that
the signature is fine (that's what xmlsec1 said), and gets the assertion data from the
assertion that contains the signature - but that assertion was never validated. The
issue is that pysaml2 does not enforce the constrains on the signature validation
facilities of xmldsig-core, that the saml-core spec defines.

The solution is simple; all we need is to make sure that assertions with signatures (1)
contain one reference element that (2) has a URI attribute (3) that is an anchor that
(4) points to the assertion in which the signature is embedded. If those conditions are
met then we're good, otherwise we should fail the verification.

Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
repository: repo_url: https://github.com/IdentityPython/pysaml2
repo_name: IdentityPython/pysaml2
description: Python implementation of SAML2
repo_language: Python
owner: IdentityPython
file_changes: filename: sigver.py
change_type: ModificationType.MODIFY
diff: @@ -1476,6 +1476,55 @@ class SecurityContext(object):
         if not certs:
             raise MissingKey(_issuer)
 
+        # saml-core section "5.4 XML Signature Profile" defines constrains on the
+        # xmldsig-core facilities. It explicitly dictates that enveloped signatures
+        # are the only signatures allowed. This mean that:
+        # * Assertion/RequestType/ResponseType elements must have an ID attribute
+        # * signatures must have a single Reference element
+        # * the Reference element must have a URI attribute
+        # * the URI attribute contains an anchor
+        # * the anchor points to the enclosing element's ID attribute
+        references = item.signature.signed_info.reference
+        signatures_must_have_a_single_reference_element = len(references) == 1
+        the_Reference_element_must_have_a_URI_attribute = (
+            signatures_must_have_a_single_reference_element
+            and hasattr(references[0], "uri")
+        )
+        the_URI_attribute_contains_an_anchor = (
+            the_Reference_element_must_have_a_URI_attribute
+            and references[0].uri.startswith("#")
+            and len(references[0].uri) > 1
+        )
+        the_anchor_points_to_the_enclosing_element_ID_attribute = (
+            the_URI_attribute_contains_an_anchor
+            and references[0].uri == "#{id}".format(id=item.id)
+        )
+        validators = {
+            "signatures must have a single reference element": (
+                signatures_must_have_a_single_reference_element
+            ),
+            "the Reference element must have a URI attribute": (
+                the_Reference_element_must_have_a_URI_attribute
+            ),
+            "the URI attribute contains an anchor": (
+                the_URI_attribute_contains_an_anchor
+            ),
+            "the anchor points to the enclosing element ID attribute": (
+                the_anchor_points_to_the_enclosing_element_ID_attribute
+            ),
+        }
+        if not all(validators.values()):
+            error_context = {
+                "message": "Signature failed to meet constraints on xmldsig",
+                "validators": validators,
+                "item ID": item.id,
+                "reference URI": item.signature.signed_info.reference[0].uri,
+                "issuer": _issuer,
+                "node name": node_name,
+                "xml document": decoded_xml,
+            }
+            raise SignatureError(error_context)
+
         verified = False
         last_pem_file = None
 

programming_language: Python
filename: saml2_response_xsw.xml
change_type: ModificationType.ADD
diff: @@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ns0:Response xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="http://lingon.catalogix.se:8087/" ID="id-vqOQ72JCppXaBWnBE" InResponseTo="id12" IssueInstant="2019-12-20T12:15:16Z" Version="2.0"><ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">urn:mace:example.com:saml:roland:idp</ns1:Issuer><ns0:Status><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></ns0:Status><ns1:Assertion ID="id-SPOOFED_ASSERTION" IssueInstant="2019-12-20T12:15:16Z" Version="2.0"><ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">urn:mace:example.com:saml:roland:idp</ns1:Issuer><ns2:Signature Id="Signature2"><ns2:SignedInfo><ns2:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ns2:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ns2:Reference URI="#id-Aa9IWfDxJVIX6GQye"><ns2:Transforms><ns2:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ns2:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ns2:Transforms><ns2:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ns2:DigestValue>EWBvQUlrwQbtrAjuUXkSBAVsZ50=</ns2:DigestValue></ns2:Reference></ns2:SignedInfo><ns2:SignatureValue>m4zRgTWleMcx1dFboeiYlbiDigHWAVhHVa+GLN++ELNMFDutuzBxc3tu6okyaNQGW3leu32wzbfdpb5+3RlpGoKj2wPX570/EMJj4uw91XfXsZfpNP+5GlgNT8w/elDmBXhG/KwmSO477Imk0szKovTBMVHmo3QOd+ba//dVsJE=</ns2:SignatureValue><ns2:KeyInfo><ns2:X509Data><ns2:X509Certificate>MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaNefiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0GA1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJsiojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSwmDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6mrPzGzk3ECbupFnqyREH3+ZPSdk=</ns2:X509Certificate></ns2:X509Data></ns2:KeyInfo></ns2:Signature><ns1:Subject><ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" NameQualifier="" SPNameQualifier="id12">ANOTHER_ID</ns1:NameID><ns1:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><ns1:SubjectConfirmationData InResponseTo="id12" NotOnOrAfter="2019-12-20T12:20:16Z" Recipient="http://lingon.catalogix.se:8087/"/></ns1:SubjectConfirmation></ns1:Subject><ns1:Conditions NotBefore="2019-12-20T12:15:16Z" NotOnOrAfter="2019-12-20T12:20:16Z"><ns1:AudienceRestriction><ns1:Audience>urn:mace:example.com:saml:roland:sp</ns1:Audience></ns1:AudienceRestriction></ns1:Conditions><ns1:AuthnStatement AuthnInstant="2019-12-20T12:15:16Z" SessionIndex="id-eEhNCc5BSiesVOl8B"><ns1:AuthnContext><ns1:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPassword</ns1:AuthnContextClassRef><ns1:AuthenticatingAuthority>http://www.example.com/login</ns1:AuthenticatingAuthority></ns1:AuthnContext></ns1:AuthnStatement><ns1:AttributeStatement><ns1:Attribute FriendlyName="eduPersonAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">staff</ns1:AttributeValue><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">ADMIN</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="mail" Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">HACKER@gmail.com</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="givenName" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">Derek</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="surName" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">Jeter</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="title" Name="urn:oid:2.5.4.12" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">shortstop</ns1:AttributeValue></ns1:Attribute></ns1:AttributeStatement></ns1:Assertion>
+<XSW_ATTACK>
+<ns1:Assertion ID="id-Aa9IWfDxJVIX6GQye" IssueInstant="2019-12-20T12:15:16Z" Version="2.0"><ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">urn:mace:example.com:saml:roland:idp</ns1:Issuer><ns1:Subject><ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" NameQualifier="" SPNameQualifier="id12">ac5b22bb8eac4a26ed07a55432a0fe0da243f6e911aa614cff402c44d7cdec36</ns1:NameID><ns1:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><ns1:SubjectConfirmationData InResponseTo="id12" NotOnOrAfter="2019-12-20T12:20:16Z" Recipient="http://lingon.catalogix.se:8087/"/></ns1:SubjectConfirmation></ns1:Subject><ns1:Conditions NotBefore="2019-12-20T12:15:16Z" NotOnOrAfter="2019-12-20T12:20:16Z"><ns1:AudienceRestriction><ns1:Audience>urn:mace:example.com:saml:roland:sp</ns1:Audience></ns1:AudienceRestriction></ns1:Conditions><ns1:AuthnStatement AuthnInstant="2019-12-20T12:15:16Z" SessionIndex="id-eEhNCc5BSiesVOl8B"><ns1:AuthnContext><ns1:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPassword</ns1:AuthnContextClassRef><ns1:AuthenticatingAuthority>http://www.example.com/login</ns1:AuthenticatingAuthority></ns1:AuthnContext></ns1:AuthnStatement><ns1:AttributeStatement><ns1:Attribute FriendlyName="eduPersonAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">staff</ns1:AttributeValue><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">member</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="mail" Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">foo@gmail.com</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="givenName" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">Derek</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="surName" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">Jeter</ns1:AttributeValue></ns1:Attribute><ns1:Attribute FriendlyName="title" Name="urn:oid:2.5.4.12" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><ns1:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">shortstop</ns1:AttributeValue></ns1:Attribute></ns1:AttributeStatement></ns1:Assertion>
+</XSW_ATTACK>
+</ns0:Response>

programming_language: PowerShell
filename: test_xsw.py
change_type: ModificationType.ADD
diff: @@ -0,0 +1,44 @@
+from datetime import datetime
+from unittest.mock import Mock
+from unittest.mock import patch
+
+from saml2.config import config_factory
+from saml2.response import authn_response
+from saml2.sigver import SignatureError
+
+from dateutil import parser
+
+from pytest import raises
+
+from pathutils import dotname
+from pathutils import full_path
+
+
+XML_RESPONSE_XSW = full_path("saml2_response_xsw.xml")
+
+
+class TestAuthnResponse:
+    def setup_class(self):
+        self.conf = config_factory("sp", dotname("server_conf"))
+        self.ar = authn_response(self.conf, "http://lingon.catalogix.se:8087/")
+
+    @patch('saml2.response.validate_on_or_after', return_value=True)
+    def test_verify_signed_xsw(self, mock_validate_on_or_after):
+        self.ar.issue_instant_ok = Mock(return_value=True)
+
+        with open(XML_RESPONSE_XSW) as fp:
+            xml_response = fp.read()
+
+        self.ar.outstanding_queries = {"id12": "http://localhost:8088/sso"}
+        self.ar.timeslack = 10000
+        self.ar.loads(xml_response, decode=False)
+
+        assert self.ar.came_from == 'http://localhost:8088/sso'
+        assert self.ar.session_id() == "id12"
+        assert self.ar.issuer() == 'urn:mace:example.com:saml:roland:idp'
+
+        with raises(SignatureError):
+            self.ar.verify()
+
+        assert self.ar.ava is None
+        assert self.ar.name_id is None

programming_language: Python