This is the fourth post of a series on Single Sign-On and OpenID Connect 1.0 security. In this post, SSRF vulnerabilities that were discovered in popular OIDC implementations (Keycloak (CVE-2020-10770) and Amazon Cognito) are explained in detail.
The series consists of following posts:
- [Overview] Common Issue Patterns and Derived Security Considerations
- [Implementation] Login Confusion
- [Implementation] Injection of CRLF sequences
- [Implementation] SSRF issues in real-life OIDC implementations
- [Specification] Redirect URI Schemes
- [Specification] Reusable State parameter
- [Responsible Disclosure] Lessons learned during Responsible Disclosure of OIDC/OAuth related issues
As you can see, we structure this series in an [Overview], attacks with concrete examples categorized in [Implementation] and [Specification], and finally lessons learned during the [Responsible Disclosure].
Note that this post gives detailed insights into SSRF vulnerabilities that were encountered in real-life OpenID Connect implementations, namely Keycloak and Amazon Cognito.
We made the observations within this advisory during the execution of my master’s thesis on “Single Sign-On Security: Security Analysis of real-life OpenID Connect Implementations”. The thesis was written at the chair for network and data security of Ruhr University Bochum.
The advisors of my thesis are Dr.-Ing. Christian Mainka (@CheariX), Dr.-Ing. Vladislav Mladenov (@v_mladenov) and Prof. Dr. Jörg Schwenk (@JoergSchwenk). Huge “Thank you” for your continuous support! 🙂
Server-Side-Request-Forgery (SSRF) is a web application vulnerability. If a malicious entity exploits such a vulnerability, “the attacker can abuse functionality on the server to read or update internal resources”, according to OWASP .
In the context of OpenID Connect, there are multiple known and previously discussed pitfalls that could enable SSRF vulnerabilities, e.g.:
request_uri parameter: Fett et al. outlined in 2017 that the
request_uri allows unauthenticated SSRF [3, Section III; A. 8)]. If this parameter is implemented, the Identity Provider is supposed to fetch a resource to obtain the Request Object. As a result, if the Identity Provider implements the
request_uri parameter, an unauthenticated attacker can launch a SSRF attack against the victim Identity Provider.
Backchannel Communication/Malicious Endpoints: In an OIDC scenario there is direct Server-to-Server communication. If the OpenID Connect Discovery is used, an Identity Provider can specify its endpoints that are used as the target of requests within genuine OpenID Connect flows. Therefore, a malicious Identity Provider could specify malicious endpoints as its OIDC endpoints during discovery, tricking a benign Service Provider into sending requests to unintended destinations. As pointed out by Mladenov and Mainka in , if there are no restrictions regarding the internal infrastructure or localhost, such a SSRF vulnerability can be used to “[…] (1.) gather information about the Intranet infrastructure of the Client, and (2.) disseminate attack vectors” .
Example 1: Unauthenticated SSRF in Keycloak (CVE-2020-10770)
Keycloak is an open-source Identity and Access Management Software (IAM) maintained by Red Hat. In addition, Keycloak is used as a base for Red Hat’s Red Hat Single Sign-On.
The attacker model applied for the following attack is an unauthenticated web attacker model introduced by Barth et al. in 2008 . In the following, the malicious entity targets a benign Identity Provider. The attacker’s objective is to gain access to internal hosts that are situated within the internal network; direct access to these hosts is restricted using a firewall.
request_uri is an optional parameter within the OpenID Connect Authentication Request that allows specifying an external URI where the Request Object can be found. Fett et al. discovered in 2017 [3, Section III; A. 8)] that, as the Identity Provider is supposed to request the external Request Object, this parameter can easily launch an SSRF attack against the Identity Provider.
The OpenID Connect Core specification defines the
request_uri as an Authentication Request parameter that “[…] enables OpenID Connect requests to be passed by reference, rather than by value” [3, Section 6]. If a Service Provider uses this parameter, the Identity Provider retrieves the Request Object “[…] from the resource at the specified URL” [5, Section 6.2].
The Identity Provider could restrict allowed URLs by allowing the Service Provider to specify the
request_uris parameter that is an “[…] array of
request_uri values […]”, within OpenID Connect Dynamic Client Registration [6, Section 2].
If the OpenID Connect Dynamic Client Registration is used, the Identity Provider can require the Service Provider to “[…] pre-register
request_uri values using the
request_uris parameter […]” [5, Section 6.2].
Thus, only if OpenID Connect is implemented with the Registration Extension, there is a mitigation hint given by the specification.
Keycloak supports using a Request Object that is referenced externally. The “Fine Grained OpenID Connect Configuration” for a specific client allows specifying the following values for the option “Request Object Required”:
- Not required (default)
- request or
- request only
As one can observe, if an OpenID Connect Client is set-up with default settings, the default value for this option is “Not required”. There is no option to do not support a Request Object at all, as “not required” means submitting the request object is optional, but if you submit one, it is used by Keycloak.
Additionally, there is no option to manually define the
request_uris parameter during manual client registration.
Finally, using the default configuration, Keycloak does not require a Request Object, but supports passing the
request_uri parameter with an Authentication Request. As a result, Keycloak is in its default configuration for Clients vulnerable to the SSRF attack Fett et al. described in 2017 [3, Section III; A. 8)].
A straight forward PoC to trigger the SSRF issue is given with the following URL (Authentication Response):
Thus, to launch the SSRF in a real-life scenario, an attacker would obtain an Authentication Request (to get the valid
If the attacker then sends the Authentication Response GET request including valid
redirect_uri, Keycloak sends a GET request to
127.0.0.1:1234 to fetch the Request Object resulting in blind SSRF.
By measuring the response time, a malicious actor may additionally perform a port scan of localhost or internally accessible hosts. The following ports are open on localhost:
- 8080: Open, Keycloak, 1087 Bytes as default response
- 8081: Open, Mailslurper, 4086 Bytes as default response
- 5555: Closed
- 5556: Closed
Demo: An example using the Timeinator Burp Plugin  is presented below:
Keycloak responds significantly faster if the port is closed in our test. Thus, the Blind SSRF enables an attacker to learn about the internal network structure.
To mitigate the described issue, the
request_uri should be disabled in Keycloak’s default Client configuration. Furthermore, localhost and private IPs should be forbidden as
request_uri, unless explicitly enabled by an administrative user with high privileges.
request_uris whitelist should also be available and enforced during manual configuration to restrict the
- 2020-04-29: Initial Report to Red Hat via https://issues.redhat.com/projects/KEYCLOAK/.
- 2020-06-05: Red Hat triages the submitted report.
- 2020-06-11: CVE-2020-10770 is assigned.
- 2020-10-16: Red Hat is informed that this post is scheduled for 2020-11-10.
- 2020-10-19: Red Hat approves to disclose this post on the described vulnerability:
Thank you for informing about this. Since this is a Moderate impact flaw, we have 365 calendar days to publish the fix after the issue has been made public so should not be a problem publishing it on 2020-11-10.
- Internal Comment by Red Hat Employee
According to the internal ticket status, Red Hat plans to resolve this issue with the upcoming major version v12 of Keycloak.
Example 2: SSRF in Amazon Cognito (AWS)
Similar to Keycloak, Amazon Cognito implements Identity and Access Management (IAM). But in contrast Amazon Cognito is a hosted service as part of the AWS family instead of a self-hosted software.
The following vulnerability was resolved by the Amazon Cognito team in August 2020. Detailed information on the Responsible Disclosure and Remediation Process will be discussed in the following.
The attacker model that is applied in the following is a malicious administrative user. The impact of attacks under this attacker model is highly conditional, but in this case, as Amazon Cognito is a hosted service, administrative users only have access to the configuration interface. The underlying infrastructure in Amazon’s hosted environment should not be accessible to external users.
Alternatively, a malicious Identity Provider could be considered as the attacker model. The actual configuration is fetched using OpenID Connect Discovery from the Identity Provider’s Configuration Endpoint so that the endpoints are controlled by the Identity Provider.
Amazon Cognito allows specifying custom OpenID Connect Identity Providers for user pools. Within the configuration of an Identity Provider, an administrative user could choose to “Run discovery”, triggering an OpenID Connect Discovery that requests the Configuration Endpoint of the Identity Provider. Amazon Cognito did not restrict the endpoints observed using OpenID Connect Discovery regarding internal IP addresses or localhost so that SSRF to the local network and localhost was possible.
A malicious actor could utilize this to perform port scans or send nearly arbitrary HTTP requests to hosts that are intentionally not exposed to the internet. An example Configuration Response is presented in the following:
The following error messages could be used to determine which ports are internally open on localhost, as the error message sent to the
redirect_uri (https://a.com/cb?error_description=[ERROR]&error=invalid_request) differed depending on the underlying Transmission Control Protocol (TCP) connection. If the connection could not be established or Amazon Cognito received an HTTP error code in response to the Token Request, the following error messages were sent to the Identity Provider:
- If the Token Endpoint was specified as “http://localhost:22”, the error message was “Connection reset”, so that we could assume Port 22 to be open.
- If the Token Endpoint was specified as “http://localhost:20”, the error message was “Connect to 127.0.0.1:20 [/127.0.0.1] failed: Connection refused”, so that we could assume Port 20 to be closed.
- If the Token Endpoint was specified as “http://test123.ngrok.io”, the error message was “test Error – 502 error getting token”, so that we could assume that there was an HTTP request to the specified target, but the web server responded with HTTP Error Code 502.
Arbitrary HTTP requests
Besides the feedback an attacker received that enabled him to determine the connection’s status, there was no information on the actual response to the HTTP request. Thus, this gadget could be considered as blind SSRF to the local network. The attacker had no direct feedback but could still control a GET request (UserInfo Request) and a POST request (Token Request) regarding scheme (http or https), host (potentially localhost or internal IP), path and query parameters.
Combining port scan and blind SSRF, a malicious administrative user or malicious Identity Provider could:
- Gather information: Which ports are open on localhost? Are there other hosts accessible? If HTTP error codes are reflected, which web service is running at this destination (fingerprinting)?
- If a service could be identified: Use blind SSRF to target an internally accessible service directly.
A blacklist is never perfect. Nevertheless, Amazon Cognito should introduce a restriction for internal IPs and localhost as OpenID Connect endpoints, as there is no legitimate use-case in the context of a hosted service.
The fix that was applied in late August 2020 mitigates the above described vulnerability.
Mitigations for the described issues were introduced by AWS / Amazon Cognito soon after we reported the issue on 2020-08-12.
- 2020-08-12: Initial report to firstname.lastname@example.org.
- 2020-08-19: Call with AWS Security Team and Amazon Cognito Developers.
- 2020-08-??: A fix is applied at the end of August 2020. As Amazon Cognito is a hosted service, we could not determine the exact date.
- 2020-10-21: AWS Security Team acknowledges that the blog post is scheduled for 2020-11-10 and offers feedback for the draft prior to publication:
Thanks again for bringing your security concern to our attention. We greatly appreciate and encourage reports from the security community.
I understand you wish to do a write up on this - would you be interested in sharing your draft with us before publishing so we may provide any assistance and feedback?
- 2020-11-05: After providing an initial draft, the AWS Security team would like to explicitly outline that the described vulnerability was mitigated shortly after we reported it responsibly. Thank you for your feedback!
Thank you for reading this post! If you have any feedback, feel free to contact me on Twitter: @_lauritz_. 👨💻
You can directly tweet about this post using this link. 🤓
Special thanks to Dr.-Ing. Christian Mainka (@CheariX), Dr.-Ing. Vladislav Mladenov (@v_mladenov), Louis Jannett (@iphoneintosh) and the AWS Security / Amazon Cognito Development Team for your feedback on this post prior to publication! 🙂