This is the third post of a series on Single Sign-On and OpenID Connect 1.0 security. In this post, a more common CRLF injection in the context of OIDC is discussed in detail. We present issues discovered in GitLab (Severity: High - Critical) and Bitbucket Server (Severity: Informative - Low).
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, the series is structured in an [Overview], attacks with concrete examples categorized as [Implementation] flaws and [Specification] flaws, and finally lessons learned during the [Responsible Disclosure].
Note that this post gives detailed insights into CRLF injections in the context of OpenID Connect.
We made the observations within this advisory during the research for 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! 🙂
This paragraph outlines some foundations needed for this post in short. If you have basic knowledge about HTTP, CRLF injections, and generic Single Sign-On scenarios, you may safely skip this chapter and directly continue with the attack description.
The Hypertext Transfer Protocol (HTTP) is the foundation for the World Wide Web as we know it. Since HTTP 1.0 (RFC1945), the protocol differentiates between body and header. In doing so, headers are terminated using CRLF sequences.
Carriage Return (\r) Line Feed (\n) sequences are commonly shortened as CRLF. They are a relic from ancient times and used to mark a newline. In the context of HTTP headers, as mentioned before, this sequence terminates a header field.
If an implementation uses third-party-controlled contents in HTTP headers without stripping out CRLF sequences, this leads to CRLF injections. The malicious entity can craft payloads that split the header field and inject additional attacker-controlled header fields.
According to OWASP , common vulnerabilities that result from CLRF injections are:
- HTTP Response Splitting: An attacker splits the HTTP response that is sent from a server. As a result, the injected contents after the CRLF sequence(s) are treated as markup and Cross-Site Scripting vulnerabilities may arise.
- Log Injections: If third-party-controlled contents are written to application log files without stripping CRLF sequences, a Log injection may occur. The possible consequences are highly conditional regarding further processing of the log file.
A beforehand not thoroughly discussed additional possible consequence of CRLF injections is HTTP Request Splitting. This behavior may occur if third-party-controlled contents are used in the context of HTTP headers that are sent along with requests that are issued by a server. This scenario may sound quite unlikely at first, but this is not the case, as the following post will outline.
In an OpenID Connect 1.0 (OIDC) scenario (as well as in OAuth 2.0), there is direct server-to-server communication between the Service Provider and the Identity Provider (commonly described as “backchannel communication”). The UserInfo Request for instance is sent directly to the Identity Provider’s UserInfo Endpoint [2, Section 5.3.].
In doing so, the Service Provider includes the Identity Provider provided
access_token within the context of the UserInfo Request’s HTTP headers for authorization purposes. If the Service Provider does not strip CRLF sequences out of the bearer token’s value, this potentially results in CRLF Injections. The actual impact depends on the implementation, an example of such a vulnerability is presented within the following figure:
Note that in the above example the target of the UserInfo Request was not altered. If OpenID Connect Discovery is used, the Identity Provider can specify arbitrary endpoints as UserInfo Endpoint.
The malicious Identity Provider attacker model was introduced in 2016 by Mainka, Mladenov, and Schwenk . They highlighted that the Identity Provider in traditional Single Sign-On setups like Kerberos is a trusted party. If customers can configure own Identity Providers or Identity Providers are dynamically discovered, these entities must be considered as third parties and must be treated accordingly.
- Victim: A malicious Identity Provider could either target an End-User or a benign Service Provider as a victim.
- Objectives of the Attacker: If the malicious Identity Provider targets an End-User, the attacker’s objective is to bypass the authentication of the End-User that used an honest Identity Provider to register at a Service Provider, to authenticate as the victim user. If a malicious Identity Provider directly targets a benign Service Provider, besides the authentication bypass, causing harm to the underlying application or infrastructure can be a malicious actor’s objective.
- Capabilities of the Attacker: A malicious Identity Provider is able to perform a genuine OpenID Connect flow and to authenticate users for a Relying Party. In doing so, it is not bound to restrictions being made by the specification and could also act maliciously. For instance, the malicious Identity Provider may send malformed requests, misleadingly set
id_token claims, or tamper with the key references used for signature validation. The prerequisite for this is either a victim Client supporting Dynamic OpenID Provider Discovery, for example, using the WebFinger protocol , or a manually configured and initially trusted Identity Provider turns rogue at some point in time. In general, as per the overall setup of Single Sign-On, the Identity Provider is a third party from a Client’s perspective.
Example 1: GitLab - Request Splitting
Suppose GitLab is configured to use an external OpenID Connect login provider. In that case, it obtains the
access_token from the Identity Provider’s Token Endpoint and redeems this token as bearer token when requesting the Identity Provider’s UserInfo Endpoint. In doing so, GitLab does not correctly sanitize the token value for usage in the context of HTTP headers.
As a result, a malicious Identity Provider can inject CRLF sequences into the
access_token that is then directly injected into the request performed by GitLab, resulting in HTTP header injection and HTTP request splitting. Additionally, the malicious Identity Provider can then arbitrarily choose the target of the UserInfo Request, without restrictions regarding localhost or private IPs within the Configuration Response. The malicious Identity Provider can then send arbitrary HTTP requests (verb, headers, body) to any internal hosts, external hosts, or localhost.
Recap of the generic attack flow as presented earlier:
Note that the target of the UserInfo Request was not altered (for illustrative purposes) but could point to an arbitrary host.
Suppose a Redis instance is present either on localhost or within the internal network. In that case, this attack’s impact can even be escalated to Remote Code Execution, as it was previously shown for other SSRF vulnerabilities with the ability to add HTTP headers in the context of GitLab .
The users who can configure the identity providers are administrative users and are in a high level of trust within a company. Thus, there do not appear to be any security implications as a direct result of this behavior that involves rogue IdPs.
- 2020-07-03: GitLab closes the report as “informative”.
Example 2: Bitbucket - Log Injection
If Bitbucket uses an OIDC “SSO 2.0” authentication provider and a custom “Username claim” is specified, Bitbucket queries the UserInfo Endpoint using the previously obtained
access_token. In doing so, Bitbucket fails to properly sanitize the bearer token before setting the HTTP header to its value. As a result, if the token includes a CRLF sequence, an unhandled exception occurs in
sun.net.www.protocol.http.HttpURLConnection.checkMessageHeader caused by illegal characters in the context of HTTP headers. This exception, including the
access_token is written in plain text and without stripping sensitive information or dangerous characters to the application log file situated at
An example is shown below:
2020-07-09 21:31:23,098 ERROR [http-nio-7990-exec-10] o.a.c.c.C.[.[.[/].[plugins] Servlet.service() for servlet [plugins] in context with path [...] threw exception
java.lang.IllegalArgumentException: Illegal character(s) in message header value: Bearer AccessTok[HERE IS THE CRLF SEQUENCE]
at sun.net.www.protocol.http.HttpURLConnection .checkMessageHeader(HttpURLConnection.java:542)
Besides the storage of sensitive information in log files, this yields additional security risks. Bitbucket comes with a GUI log analyzer that parses the application log. Using the log injection, it is possible to spoof log entries that are rendered and presented to administrative users.
As the log analyzer uses regular expressions to search for known issues and only renders a trusted description and markup (no attacker-controlled contents), no further injection issues within the frontend representation of injected log entries could be observed. Nevertheless, if the log analyzer is configured to run periodically and sends mails on recognized errors, a malicious actor can trick Bitbucket to send notification emails to administrative users.
- 2020-06-10: Initial submission via https://bugcrowd.com/atlassian.
- 2020-06-17: Report passes triage and is forwarded to Atlassian.
- 2020-06-28: Atlassian closes the report as “Not Applicable”
We have deemed that this report is not applicable. Since an attacker would need to have administrator permission to configure malicious IdP which is higher level of privileges which achieved by this attack.
RFC6750 specifies the allowed character-set for bearer credentials in the context of HTTP header . OIDC implementations must implement this as whitelist - excluded characters must be stripped from the
access_token before it is used in sensitive contexts:
The syntax for Bearer credentials is as follows:
b64token = 1*( ALPHA / DIGIT / “-” / “.” / “_” / “~” / “+” / “/” ) *"="
credentials = “Bearer” 1*SP b64token
Thank you for reading this post! Stay tuned for the following posts of this series, as they will include more examples and detailed explanations of vulnerabilities that were found within popular real-life OpenID Connect Implementations.
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) and Louis Jannett (@iphoneintosh) for your feedback on this post prior to publication! 🙂